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