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 "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace readability {
19 
20 void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) {
21   const auto DeleteExpr =
22       cxxDeleteExpr(has(castExpr(has(declRefExpr(
23                         to(decl(equalsBoundNode("deletedPointer"))))))))
24           .bind("deleteExpr");
25 
26   const auto DeleteMemberExpr =
27       cxxDeleteExpr(has(castExpr(has(memberExpr(hasDeclaration(
28                         fieldDecl(equalsBoundNode("deletedMemberPointer"))))))))
29           .bind("deleteMemberExpr");
30 
31   const auto PointerExpr = ignoringImpCasts(anyOf(
32       declRefExpr(to(decl().bind("deletedPointer"))),
33       memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer")))));
34 
35   const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean),
36                                          hasSourceExpression(PointerExpr));
37   const auto BinaryPointerCheckCondition =
38       binaryOperator(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))),
39                      hasEitherOperand(PointerExpr));
40 
41   Finder->addMatcher(
42       ifStmt(hasCondition(anyOf(PointerCondition, BinaryPointerCheckCondition)),
43              hasThen(anyOf(
44                  DeleteExpr, DeleteMemberExpr,
45                  compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)),
46                               statementCountIs(1))
47                      .bind("compound"))))
48           .bind("ifWithDelete"),
49       this);
50 }
51 
52 void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
53   const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
54   const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
55 
56   auto Diag = diag(
57       IfWithDelete->getBeginLoc(),
58       "'if' statement is unnecessary; deleting null pointer has no effect");
59   if (IfWithDelete->getElse())
60     return;
61   // FIXME: generate fixit for this case.
62 
63   Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
64       IfWithDelete->getBeginLoc(),
65       Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getEndLoc(), 0,
66                                  *Result.SourceManager,
67                                  Result.Context->getLangOpts())));
68   if (Compound) {
69     Diag << FixItHint::CreateRemoval(
70         CharSourceRange::getTokenRange(Compound->getLBracLoc()));
71     Diag << FixItHint::CreateRemoval(
72         CharSourceRange::getTokenRange(Compound->getRBracLoc()));
73   }
74 }
75 
76 } // namespace readability
77 } // namespace tidy
78 } // namespace clang
79