clang-tools 23.0.0git
DeleteNullPointerCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "../utils/LexerUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14
15using namespace clang::ast_matchers;
16
18
20 const auto DeleteExpr =
21 cxxDeleteExpr(
22 has(declRefExpr(to(decl(equalsBoundNode("deletedPointer"))))))
23 .bind("deleteExpr");
24
25 const auto DeleteMemberExpr =
26 cxxDeleteExpr(has(memberExpr(hasDeclaration(
27 fieldDecl(equalsBoundNode("deletedMemberPointer"))))))
28 .bind("deleteMemberExpr");
29
30 const auto PointerExpr = anyOf(
31 declRefExpr(to(decl().bind("deletedPointer"))),
32 memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer"))));
33
34 const auto BinaryPointerCheckCondition = binaryOperator(hasOperands(
35 anyOf(cxxNullPtrLiteralExpr(), integerLiteral(equals(0))), PointerExpr));
36
37 const auto IfWithScopedCondition =
38 ifStmt(anyOf(hasInitStatement(stmt()),
39 hasConditionVariableStatement(declStmt())))
40 .bind("ifWithScopedCondition");
41
42 Finder->addMatcher(
43 ifStmt(hasCondition(anyOf(PointerExpr, BinaryPointerCheckCondition)),
44 optionally(IfWithScopedCondition),
45 hasThen(anyOf(
46 DeleteExpr, DeleteMemberExpr,
47 compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)),
48 statementCountIs(1))
49 .bind("compound"))))
50 .bind("ifWithDelete"),
51 this);
52}
53
54void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
55 const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
56 const bool HasScopedCondition =
57 Result.Nodes.getNodeAs<IfStmt>("ifWithScopedCondition") != nullptr;
58 const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
59
60 auto Diag = diag(
61 IfWithDelete->getBeginLoc(),
62 "'if' statement is unnecessary; deleting null pointer has no effect");
63 if (HasScopedCondition || IfWithDelete->hasElseStorage())
64 return;
65 // FIXME: generate fixit for this case.
66
67 const std::optional<Token> PrevTok = utils::lexer::getPreviousToken(
68 IfWithDelete->getThen()->getBeginLoc(), *Result.SourceManager,
69 Result.Context->getLangOpts());
70 if (!PrevTok)
71 return;
72
73 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
74 IfWithDelete->getBeginLoc(), PrevTok->getLocation()));
75
76 if (Compound) {
77 Diag << FixItHint::CreateRemoval(
78 CharSourceRange::getTokenRange(Compound->getLBracLoc()));
79 Diag << FixItHint::CreateRemoval(
80 CharSourceRange::getTokenRange(Compound->getRBracLoc()));
81 }
82}
83
84} // namespace clang::tidy::readability
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
std::optional< Token > getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or std::nullopt if not found.