1 //===--- BoolPointerImplicitConversionCheck.cpp - clang-tidy --------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "BoolPointerImplicitConversionCheck.h"
11 
12 using namespace clang::ast_matchers;
13 
14 namespace clang {
15 namespace tidy {
16 namespace bugprone {
17 
18 void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
19   // Look for ifs that have an implicit bool* to bool conversion in the
20   // condition. Filter negations.
21   Finder->addMatcher(
22       ifStmt(hasCondition(findAll(implicitCastExpr(
23                  unless(hasParent(unaryOperator(hasOperatorName("!")))),
24                  hasSourceExpression(
25                      expr(hasType(pointerType(pointee(booleanType()))),
26                           ignoringParenImpCasts(declRefExpr().bind("expr")))),
27                  hasCastKind(CK_PointerToBoolean)))),
28              unless(isInTemplateInstantiation()))
29           .bind("if"),
30       this);
31 }
32 
33 void BoolPointerImplicitConversionCheck::check(
34     const MatchFinder::MatchResult &Result) {
35   auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
36   auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
37 
38   // Ignore macros.
39   if (Var->getBeginLoc().isMacroID())
40     return;
41 
42   // Only allow variable accesses for now, no function calls or member exprs.
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   const Decl *D = Var->getDecl();
48   auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
49   if (!match(findAll(
50                  unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
51              *If, *Result.Context)
52            .empty() ||
53       !match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
54              *Result.Context)
55            .empty() ||
56       // FIXME: We should still warn if the paremater is implicitly converted to
57       // bool.
58       !match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
59              *If, *Result.Context)
60            .empty() ||
61       !match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
62              *If, *Result.Context)
63            .empty())
64     return;
65 
66   diag(Var->getBeginLoc(), "dubious check of 'bool *' against 'nullptr', did "
67                            "you mean to dereference it?")
68       << FixItHint::CreateInsertion(Var->getBeginLoc(), "*");
69 }
70 
71 } // namespace bugprone
72 } // namespace tidy
73 } // namespace clang
74