1 //===--- SignedBitwiseCheck.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 "SignedBitwiseCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 using namespace clang::ast_matchers::internal; 16 17 namespace clang { 18 namespace tidy { 19 namespace hicpp { 20 21 void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { 22 const auto SignedIntegerOperand = 23 expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed-operand"); 24 25 // The standard [bitmask.types] allows some integral types to be implemented 26 // as signed types. Exclude these types from diagnosing for bitwise or(|) and 27 // bitwise and(&). Shifting and complementing such values is still not 28 // allowed. 29 const auto BitmaskType = namedDecl(anyOf( 30 hasName("::std::locale::category"), hasName("::std::ctype_base::mask"), 31 hasName("::std::ios_base::fmtflags"), hasName("::std::ios_base::iostate"), 32 hasName("::std::ios_base::openmode"))); 33 const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType))); 34 35 // Match binary bitwise operations on signed integer arguments. 36 Finder->addMatcher( 37 binaryOperator( 38 allOf(anyOf(hasOperatorName("^"), hasOperatorName("|"), 39 hasOperatorName("&")), 40 41 unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))), 42 43 hasEitherOperand(SignedIntegerOperand), 44 hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))) 45 .bind("binary-no-sign-interference"), 46 this); 47 48 // Shifting and complement is not allowed for any signed integer type because 49 // the sign bit may corrupt the result. 50 Finder->addMatcher( 51 binaryOperator(allOf(anyOf(hasOperatorName("<<"), hasOperatorName(">>")), 52 hasEitherOperand(SignedIntegerOperand), 53 hasLHS(hasType(isInteger())), 54 hasRHS(hasType(isInteger())))) 55 .bind("binary-sign-interference"), 56 this); 57 58 // Match unary operations on signed integer types. 59 Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"), 60 hasUnaryOperand(SignedIntegerOperand))) 61 .bind("unary-signed"), 62 this); 63 } 64 65 void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { 66 const ast_matchers::BoundNodes &N = Result.Nodes; 67 const auto *SignedOperand = N.getNodeAs<Expr>("signed-operand"); 68 assert(SignedOperand && 69 "No signed operand found in problematic bitwise operations"); 70 71 bool IsUnary = false; 72 SourceLocation Location; 73 74 if (const auto *UnaryOp = N.getNodeAs<UnaryOperator>("unary-signed")) { 75 IsUnary = true; 76 Location = UnaryOp->getLocStart(); 77 } else { 78 if (const auto *BinaryOp = 79 N.getNodeAs<BinaryOperator>("binary-no-sign-interference")) 80 Location = BinaryOp->getLocStart(); 81 else if (const auto *BinaryOp = 82 N.getNodeAs<BinaryOperator>("binary-sign-interference")) 83 Location = BinaryOp->getLocStart(); 84 else 85 llvm_unreachable("unexpected matcher result"); 86 } 87 88 diag(Location, 89 "use of a signed integer operand with a %select{binary|unary}0 bitwise " 90 "operator") 91 << IsUnary << SignedOperand->getSourceRange(); 92 } 93 94 } // namespace hicpp 95 } // namespace tidy 96 } // namespace clang 97