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 
registerMatchers(MatchFinder * Finder)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 
checkImpl(const MatchFinder::MatchResult & Result,const Expr * Ref,const IfStmt * If,const ast_matchers::internal::Matcher<Expr> & RefMatcher,ClangTidyCheck & Check)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 
check(const MatchFinder::MatchResult & Result)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