diff options
author | Benjamin Kramer <benny.kra@googlemail.com> | 2014-07-11 08:08:47 +0000 |
---|---|---|
committer | Benjamin Kramer <benny.kra@googlemail.com> | 2014-07-11 08:08:47 +0000 |
commit | 1c8b31753b0699960d314a67dba2c71ba832e6ae (patch) | |
tree | baa10f2e41cb0f56787cb9fe1776011537c8211b /clang-tools-extra | |
parent | 780ce0f8e3505e7a3df0ee5f5d421d8e0e253079 (diff) | |
download | bcm5719-llvm-1c8b31753b0699960d314a67dba2c71ba832e6ae.tar.gz bcm5719-llvm-1c8b31753b0699960d314a67dba2c71ba832e6ae.zip |
[clang-tidy] Add a checker for implicit bool conversion of a bool*.
The goal is to find code like the example below, which is likely a typo
where someone meant to write "if (*b)".
bool *b = SomeFunction();
if (b) {
// b never dereferenced
}
This checker naturally has a relatively high false positive rate so it
applies some heuristics to avoid cases where the pointer is checked for
nullptr before being written.
Differential Revision: http://reviews.llvm.org/D4458
llvm-svn: 212797
Diffstat (limited to 'clang-tools-extra')
5 files changed, 195 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.cpp b/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.cpp new file mode 100644 index 00000000000..6565874a340 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.cpp @@ -0,0 +1,70 @@ +//===--- BoolPointerImplicitConversion.cpp - clang-tidy -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BoolPointerImplicitConversion.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace ast_matchers { + +AST_MATCHER(CastExpr, isPointerToBoolean) { + return Node.getCastKind() == CK_PointerToBoolean; +} +AST_MATCHER(QualType, isBoolean) { return Node->isBooleanType(); } + +} // namespace ast_matchers + +namespace tidy { + +void BoolPointerImplicitConversion::registerMatchers(MatchFinder *Finder) { + // Look for ifs that have an implicit bool* to bool conversion in the + // condition. Filter negations. + Finder->addMatcher( + ifStmt(hasCondition(findAll(implicitCastExpr( + allOf(unless(hasParent(unaryOperator(hasOperatorName("!")))), + hasSourceExpression(expr( + hasType(pointerType(pointee(isBoolean()))), + ignoringParenImpCasts(declRefExpr().bind("expr")))), + isPointerToBoolean()))))).bind("if"), + this); +} + +void +BoolPointerImplicitConversion::check(const MatchFinder::MatchResult &Result) { + auto *If = Result.Nodes.getStmtAs<IfStmt>("if"); + auto *Var = Result.Nodes.getStmtAs<DeclRefExpr>("expr"); + + // Only allow variable accesses for now, no function calls or member exprs. + // Check that we don't dereference the variable anywhere within the if. This + // avoids false positives for checks of the pointer for nullptr before it is + // dereferenced. If there is a dereferencing operator on this variable don't + // emit a diagnostic. Also ignore array subscripts. + const Decl *D = Var->getDecl(); + auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D)))); + if (!match(findAll( + unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))), + *If, *Result.Context).empty() || + !match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If, + *Result.Context).empty() || + // FIXME: We should still warn if the paremater is implicitly converted to + // bool. + !match(findAll(callExpr(hasAnyArgument(DeclRef))), *If, *Result.Context) + .empty() || + !match(findAll(deleteExpr(has(expr(DeclRef)))), *If, *Result.Context) + .empty()) + return; + + diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did " + "you mean to dereference it?") + << FixItHint::CreateInsertion(Var->getLocStart(), "*"); +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.h b/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.h new file mode 100644 index 00000000000..16c0d631eec --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/BoolPointerImplicitConversion.h @@ -0,0 +1,34 @@ +//===--- BoolPointerImplicitConversion.h - clang-tidy -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOL_POINTER_IMPLICIT_CONV_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOL_POINTER_IMPLICIT_CONV_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks for conditions based on implicit conversion from a bool +/// pointer to bool e.g. +/// bool *p; +/// if (p) { +/// // Never used in a pointer-specific way. +/// } +class BoolPointerImplicitConversion : public ClangTidyCheck { +public: + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOL_POINTER_IMPLICIT_CONV_H + diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index 796dc15062d..ba8430c3ceb 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp + BoolPointerImplicitConversion.cpp MiscTidyModule.cpp RedundantSmartptrGet.cpp UseOverride.cpp diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index 28a2f0be788..ee992928057 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "ArgumentCommentCheck.h" +#include "BoolPointerImplicitConversion.h" #include "RedundantSmartptrGet.h" #include "UseOverride.h" @@ -24,6 +25,9 @@ public: "misc-argument-comment", new ClangTidyCheckFactory<ArgumentCommentCheck>()); CheckFactories.addCheckFactory( + "misc-bool-pointer-implicit-conversion", + new ClangTidyCheckFactory<BoolPointerImplicitConversion>()); + CheckFactories.addCheckFactory( "misc-redundant-smartptr-get", new ClangTidyCheckFactory<RedundantSmartptrGet>()); CheckFactories.addCheckFactory( diff --git a/clang-tools-extra/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp b/clang-tools-extra/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp new file mode 100644 index 00000000000..98cfd667242 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp @@ -0,0 +1,86 @@ +// RUN: $(dirname %s)/check_clang_tidy_fix.sh %s misc-bool-pointer-implicit-conversion %t +// REQUIRES: shell + +bool *SomeFunction(); +void SomeOtherFunction(bool*); +bool F(); +void G(bool); + + +template <typename T> +void t(T b) { + if (b) { + } +} + +void foo() { + bool *b = SomeFunction(); + if (b) { +// CHECK-MESSAGES: dubious check of 'bool *' against 'nullptr' +// CHECK-FIXES: if (*b) { + } + + if (F() && b) { +// CHECK-MESSAGES: dubious check of 'bool *' against 'nullptr' +// CHECK-FIXES: if (F() && *b) { + } + + // TODO: warn here. + if (b) { + G(b); + } + +#define TESTMACRO if (b || F()) + + TESTMACRO { +// CHECK-MESSAGES: dubious check of 'bool *' against 'nullptr' +// Can't fix this. +// CHECK-FIXES: #define TESTMACRO if (b || F()) +// CHECK-FIXES: TESTMACRO { + } + +// CHECK-MESSAGES-NOT: warning: + + t(b); + + if (!b) { + // no-warning + } + + if (SomeFunction()) { + // no-warning + } + + bool *c = SomeFunction(); + if (c) { + (void)c; + (void)*c; // no-warning + } + + if (c) { + *c = true; // no-warning + } + + if (c) { + c[0] = false; // no-warning + } + + if (c) { + SomeOtherFunction(c); // no-warning + } + + if (c) { + delete[] c; // no-warning + } + + if (c) { + *(c) = false; // no-warning + } + + struct { + bool *b; + } d = { SomeFunction() }; + + if (d.b) + (void)*d.b; // no-warning +} |