Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -140,6 +140,10 @@ let ParentPackage = CoreAlpha in { +def BitwiseOpBoolArgChecker : Checker<"BitwiseOpBoolArg">, + HelpText<"Warn about bitwise operations performed on Booleans">, + DescFile<"BitwiseOpBoolArgChecker.cpp">; + def BoolAssignmentChecker : Checker<"BoolAssignment">, HelpText<"Warn about assigning non-{0,1} values to Boolean variables">, DescFile<"BoolAssignmentChecker.cpp">; Index: lib/StaticAnalyzer/Checkers/BitwiseOpBoolArgChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/BitwiseOpBoolArgChecker.cpp +++ lib/StaticAnalyzer/Checkers/BitwiseOpBoolArgChecker.cpp @@ -0,0 +1,145 @@ +//== BitwiseOpBoolArgChecker.cpp - Check for bit ops on booleans -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines BitwiseOpBoolArgChecker, which looks for bitwise operations +// performed on boolean values and suggests using logical operators instead +// where appropriate. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { +class BitwiseOpBoolArgChecker : public Checker { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; + +class Callback : public MatchFinder::MatchCallback { + const CheckerBase *C; + BugReporter &BR; + AnalysisDeclContext *AC; + + void report(const BinaryOperator *BO); + void report(const UnaryOperator *UO); + +public: + Callback(const CheckerBase *C, BugReporter &BR, AnalysisDeclContext *AC) + : C(C), BR(BR), AC(AC) {} + + virtual void run(const MatchFinder::MatchResult &Result) override; +}; +} // end anonymous namespace + +void BitwiseOpBoolArgChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + auto BitwiseOpBool = stmt(eachOf( + forEachDescendant( + binaryOperator( + anyOf(hasOperatorName("|"), hasOperatorName("&"), + hasOperatorName("<<"), hasOperatorName(">>"), + hasOperatorName("^")), + eachOf(hasLHS(ignoringParenImpCasts(hasType(booleanType()))), + hasRHS(ignoringParenImpCasts(hasType(booleanType()))))) + .bind("binOp")), + forEachDescendant(unaryOperator(hasOperatorName("~"), + hasUnaryOperand(ignoringParenImpCasts( + hasType(booleanType())))) + .bind("unOp")))); + + MatchFinder F; + Callback CB(this, BR, Mgr.getAnalysisDeclContext(D)); + + F.addMatcher(BitwiseOpBool, &CB); + F.match(*D->getBody(), Mgr.getASTContext()); +} + +void Callback::report(const BinaryOperator *BO) { + const Expr *LHS = BO->getLHS(); + const Expr *RHS = BO->getRHS(); + + QualType LType = LHS->IgnoreParenImpCasts()->getType(); + QualType RType = RHS->IgnoreParenImpCasts()->getType(); + + const bool LBool = LType.isNull() ? false : LType->isBooleanType(); + const bool RBool = RType.isNull() ? false : RType->isBooleanType(); + + StringRef OpcodeStr = BO->getOpcodeStr(); + + SmallString<96> Message; + llvm::raw_svector_ostream OS(Message); + + SmallVector SourceRangeVec; + if (LBool && RBool) { + OS << "Bitwise '" << OpcodeStr + << "' operator is applied to Boolean values."; + if (BO->isBitwiseOp()) + OS << " Did you mean '" << OpcodeStr << OpcodeStr << "'?"; + + SourceRangeVec.push_back(LHS->getSourceRange()); + SourceRangeVec.push_back(RHS->getSourceRange()); + } else { + OS << "Boolean value met at the "; + if (LBool) { + OS << "left"; + SourceRangeVec.push_back(LHS->getSourceRange()); + } else { + OS << "right"; + SourceRangeVec.push_back(RHS->getSourceRange()); + } + OS << " side of the bitwise '" << OpcodeStr << "' operator"; + } + + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createOperatorLoc(BO, BR.getSourceManager()); + + BR.EmitBasicReport(AC->getDecl(), C, "Bitwise operation on Boolean value", + categories::LogicError, Message, Loc, SourceRangeVec); +} + +void Callback::report(const UnaryOperator *UO) { + SmallString<96> Message; + llvm::raw_svector_ostream OS(Message); + OS << "Bitwise '" << UnaryOperator::getOpcodeStr(UO->getOpcode()) + << "' operator is applied to Boolean value. Did you mean '!'?"; + + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createBegin(UO, BR.getSourceManager(), AC); + + BR.EmitBasicReport(AC->getDecl(), C, "Bitwise operation on Boolean value", + categories::LogicError, Message, Loc, + UO->getSourceRange()); +} + +void Callback::run(const MatchFinder::MatchResult &Result) { + const BinaryOperator *BO = Result.Nodes.getNodeAs("binOp"); + if (BO) + report(BO); + + const UnaryOperator *UO = Result.Nodes.getNodeAs("unOp"); + if (UO) + report(UO); +} + +namespace clang { +namespace ento { +void registerBitwiseOpBoolArgChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} +} // namespace ento +} // namespace clang Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -9,6 +9,7 @@ ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp BasicObjCFoundationChecks.cpp + BitwiseOpBoolArgChecker.cpp BlockInCriticalSectionChecker.cpp BoolAssignmentChecker.cpp BuiltinFunctionChecker.cpp Index: test/Analysis/bitwise-op-bool-arg.cpp =================================================================== --- test/Analysis/bitwise-op-bool-arg.cpp +++ test/Analysis/bitwise-op-bool-arg.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.BitwiseOpBoolArg -verify -x c++ %s + +bool getBool() { + return true; +} + +bool boolBitwiseOr() { + bool v = true; + return v | 1; // expected-warning {{Boolean value met at the left side of the bitwise '|' operator}} +} + +bool boolBitwiseAnd() { + return 2 & getBool(); // expected-warning {{Boolean value met at the right side of the bitwise '&' operator}} +} + +bool ifBoolBitwise(bool v) { + if (v << 1) { // expected-warning {{Boolean value met at the left side of the bitwise '<<' operator}} + return true; + } + return false; +} + +bool ifBoolAnd(bool x, bool y) { + if (x && y) { // no-warning + return false; + } + return !y; +} + +int bitwiseInt(int x, int y) { + return x | y; // no-warning +} + +int bitwiseBoolCastedToIntOne(bool x) { + return (int)x | 1; // no-warning +} + +int bitwiseBoolCastedToIntBoth(bool x, bool y) { + return (int)x & (int)y; // no-warning +} + +int bitwiseImplCast(int x, int y) { + return !x & !y; // expected-warning {{Bitwise '&' operator is applied to Boolean values. Did you mean '&&'?}} +} + +bool logicalImplCast(int x, int y) { + return !x && !y; // no-warning +} + +bool ifBoolBitwiseAnd(bool x, bool y) { + if ((x && y) & (x || y)) { // expected-warning {{Bitwise '&' operator is applied to Boolean values. Did you mean '&&'?}} + return x || y; + } + return !(x && y); +} + +bool ifBoolBitwiseNot(bool x) { + return ~x; // expected-warning {{Bitwise '~' operator is applied to Boolean value. Did you mean '!'?}} +}