1 | //===--- BoolPointerImplicitConversionCheck.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 "BoolPointerImplicitConversionCheck.h" |
10 | |
11 | using namespace clang::ast_matchers; |
12 | |
13 | namespace clang::tidy::bugprone { |
14 | |
15 | void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) { |
16 | // Look for ifs that have an implicit bool* to bool conversion in the |
17 | // condition. Filter negations. |
18 | Finder->addMatcher( |
19 | NodeMatch: traverse( |
20 | TK: TK_AsIs, |
21 | InnerMatcher: ifStmt( |
22 | hasCondition(InnerMatcher: findAll(Matcher: implicitCastExpr( |
23 | unless(hasParent(unaryOperator(hasOperatorName(Name: "!" )))), |
24 | hasSourceExpression(InnerMatcher: expr( |
25 | hasType(InnerMatcher: pointerType(pointee(booleanType()))), |
26 | ignoringParenImpCasts(InnerMatcher: anyOf(declRefExpr().bind(ID: "expr" ), |
27 | memberExpr().bind(ID: "expr" ))))), |
28 | hasCastKind(Kind: CK_PointerToBoolean)))), |
29 | unless(isInTemplateInstantiation())) |
30 | .bind(ID: "if" )), |
31 | Action: this); |
32 | } |
33 | |
34 | static void checkImpl(const MatchFinder::MatchResult &Result, const Expr *Ref, |
35 | const IfStmt *If, |
36 | const ast_matchers::internal::Matcher<Expr> &RefMatcher, |
37 | ClangTidyCheck &Check) { |
38 | // Ignore macros. |
39 | if (Ref->getBeginLoc().isMacroID()) |
40 | return; |
41 | |
42 | // Only allow variable accesses and member exprs for now, no function calls. |
43 | // Check that we don't dereference the variable anywhere within the if. This |
44 | // avoids false positives for checks of the pointer for nullptr before it is |
45 | // dereferenced. If there is a dereferencing operator on this variable don't |
46 | // emit a diagnostic. Also ignore array subscripts. |
47 | if (!match(Matcher: findAll(Matcher: unaryOperator(hasOperatorName(Name: "*" ), |
48 | hasUnaryOperand(InnerMatcher: RefMatcher))), |
49 | Node: *If, Context&: *Result.Context) |
50 | .empty() || |
51 | !match(Matcher: findAll(Matcher: arraySubscriptExpr(hasBase(InnerMatcher: RefMatcher))), Node: *If, |
52 | Context&: *Result.Context) |
53 | .empty() || |
54 | // FIXME: We should still warn if the paremater is implicitly converted to |
55 | // bool. |
56 | !match( |
57 | Matcher: findAll(Matcher: callExpr(hasAnyArgument(InnerMatcher: ignoringParenImpCasts(InnerMatcher: RefMatcher)))), |
58 | Node: *If, Context&: *Result.Context) |
59 | .empty() || |
60 | !match( |
61 | Matcher: findAll(Matcher: cxxDeleteExpr(has(ignoringParenImpCasts(InnerMatcher: expr(RefMatcher))))), |
62 | Node: *If, Context&: *Result.Context) |
63 | .empty()) |
64 | return; |
65 | |
66 | Check.diag(Ref->getBeginLoc(), |
67 | "dubious check of 'bool *' against 'nullptr', did " |
68 | "you mean to dereference it?" ) |
69 | << FixItHint::CreateInsertion(InsertionLoc: Ref->getBeginLoc(), Code: "*" ); |
70 | } |
71 | |
72 | void BoolPointerImplicitConversionCheck::check( |
73 | const MatchFinder::MatchResult &Result) { |
74 | const auto *If = Result.Nodes.getNodeAs<IfStmt>(ID: "if" ); |
75 | if (const auto *E = Result.Nodes.getNodeAs<Expr>(ID: "expr" )) { |
76 | const Decl *D = isa<DeclRefExpr>(Val: E) ? cast<DeclRefExpr>(Val: E)->getDecl() |
77 | : cast<MemberExpr>(Val: E)->getMemberDecl(); |
78 | const auto M = |
79 | ignoringParenImpCasts(InnerMatcher: anyOf(declRefExpr(to(InnerMatcher: equalsNode(Other: D))), |
80 | memberExpr(hasDeclaration(InnerMatcher: equalsNode(Other: D))))); |
81 | checkImpl(Result, E, If, M, *this); |
82 | } |
83 | } |
84 | |
85 | } // namespace clang::tidy::bugprone |
86 | |