| 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 | |