xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
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 
registerMatchers(MatchFinder * Finder)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       traverse(
20           TK_AsIs,
21           ifStmt(
22               hasCondition(findAll(implicitCastExpr(
23                   unless(hasParent(unaryOperator(hasOperatorName("!")))),
24                   hasSourceExpression(expr(
25                       hasType(pointerType(pointee(booleanType()))),
26                       ignoringParenImpCasts(anyOf(declRefExpr().bind("expr"),
27                                                   memberExpr().bind("expr"))))),
28                   hasCastKind(CK_PointerToBoolean)))),
29               unless(isInTemplateInstantiation()))
30               .bind("if")),
31       this);
32 }
33 
checkImpl(const MatchFinder::MatchResult & Result,const Expr * Ref,const IfStmt * If,const ast_matchers::internal::Matcher<Expr> & RefMatcher,ClangTidyCheck & Check)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(findAll(unaryOperator(hasOperatorName("*"),
48                                    hasUnaryOperand(RefMatcher))),
49              *If, *Result.Context)
50            .empty() ||
51       !match(findAll(arraySubscriptExpr(hasBase(RefMatcher))), *If,
52              *Result.Context)
53            .empty() ||
54       // FIXME: We should still warn if the paremater is implicitly converted to
55       // bool.
56       !match(
57            findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(RefMatcher)))),
58            *If, *Result.Context)
59            .empty() ||
60       !match(
61            findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(RefMatcher))))),
62            *If, *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(Ref->getBeginLoc(), "*");
70 }
71 
check(const MatchFinder::MatchResult & Result)72 void BoolPointerImplicitConversionCheck::check(
73     const MatchFinder::MatchResult &Result) {
74   const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
75   if (const auto *E = Result.Nodes.getNodeAs<Expr>("expr")) {
76     const Decl *D = isa<DeclRefExpr>(E) ? cast<DeclRefExpr>(E)->getDecl()
77                                         : cast<MemberExpr>(E)->getMemberDecl();
78     const auto M =
79         ignoringParenImpCasts(anyOf(declRefExpr(to(equalsNode(D))),
80                                     memberExpr(hasDeclaration(equalsNode(D)))));
81     checkImpl(Result, E, If, M, *this);
82   }
83 }
84 
85 } // namespace clang::tidy::bugprone
86