1 //===--- DeleteNullPointerCheck.cpp - clang-tidy---------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "DeleteNullPointerCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace readability {
20
registerMatchers(MatchFinder * Finder)21 void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) {
22 const auto DeleteExpr =
23 cxxDeleteExpr(has(castExpr(has(declRefExpr(
24 to(decl(equalsBoundNode("deletedPointer"))))))))
25 .bind("deleteExpr");
26
27 const auto DeleteMemberExpr =
28 cxxDeleteExpr(has(castExpr(has(memberExpr(hasDeclaration(
29 fieldDecl(equalsBoundNode("deletedMemberPointer"))))))))
30 .bind("deleteMemberExpr");
31
32 const auto PointerExpr = ignoringImpCasts(anyOf(
33 declRefExpr(to(decl().bind("deletedPointer"))),
34 memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer")))));
35
36 const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean),
37 hasSourceExpression(PointerExpr));
38 const auto BinaryPointerCheckCondition = binaryOperator(
39 hasOperands(castExpr(hasCastKind(CK_NullToPointer)), PointerExpr));
40
41 Finder->addMatcher(
42 traverse(ast_type_traits::TK_AsIs,
43 ifStmt(hasCondition(
44 anyOf(PointerCondition, BinaryPointerCheckCondition)),
45 hasThen(anyOf(DeleteExpr, DeleteMemberExpr,
46 compoundStmt(anyOf(has(DeleteExpr),
47 has(DeleteMemberExpr)),
48 statementCountIs(1))
49 .bind("compound"))))
50 .bind("ifWithDelete")),
51 this);
52 }
53
check(const MatchFinder::MatchResult & Result)54 void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
55 const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
56 const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
57
58 auto Diag = diag(
59 IfWithDelete->getBeginLoc(),
60 "'if' statement is unnecessary; deleting null pointer has no effect");
61 if (IfWithDelete->getElse())
62 return;
63 // FIXME: generate fixit for this case.
64
65 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
66 IfWithDelete->getBeginLoc(),
67 utils::lexer::getPreviousToken(IfWithDelete->getThen()->getBeginLoc(),
68 *Result.SourceManager,
69 Result.Context->getLangOpts())
70 .getLocation()));
71
72 if (Compound) {
73 Diag << FixItHint::CreateRemoval(
74 CharSourceRange::getTokenRange(Compound->getLBracLoc()));
75 Diag << FixItHint::CreateRemoval(
76 CharSourceRange::getTokenRange(Compound->getRBracLoc()));
77 }
78 }
79
80 } // namespace readability
81 } // namespace tidy
82 } // namespace clang
83