//===--- SignedBitwiseCheck.cpp - clang-tidy-------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SignedBitwiseCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; namespace clang { namespace tidy { namespace hicpp { SignedBitwiseCheck::SignedBitwiseCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IgnorePositiveIntegerLiterals( Options.get("IgnorePositiveIntegerLiterals", false)) {} void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IgnorePositiveIntegerLiterals", IgnorePositiveIntegerLiterals); } void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { const auto SignedIntegerOperand = (IgnorePositiveIntegerLiterals ? expr(ignoringImpCasts(hasType(isSignedInteger())), unless(integerLiteral())) : expr(ignoringImpCasts(hasType(isSignedInteger())))) .bind("signed-operand"); // The standard [bitmask.types] allows some integral types to be implemented // as signed types. Exclude these types from diagnosing for bitwise or(|) and // bitwise and(&). Shifting and complementing such values is still not // allowed. const auto BitmaskType = namedDecl( hasAnyName("::std::locale::category", "::std::ctype_base::mask", "::std::ios_base::fmtflags", "::std::ios_base::iostate", "::std::ios_base::openmode")); const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType))); // Match binary bitwise operations on signed integer arguments. Finder->addMatcher( binaryOperator(hasAnyOperatorName("^", "|", "&", "^=", "|=", "&="), unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))) .bind("binary-no-sign-interference"), this); // Shifting and complement is not allowed for any signed integer type because // the sign bit may corrupt the result. Finder->addMatcher( binaryOperator(hasAnyOperatorName("<<", ">>", "<<=", ">>="), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))) .bind("binary-sign-interference"), this); // Match unary operations on signed integer types. Finder->addMatcher( unaryOperator(hasOperatorName("~"), hasUnaryOperand(SignedIntegerOperand)) .bind("unary-signed"), this); } void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { const ast_matchers::BoundNodes &N = Result.Nodes; const auto *SignedOperand = N.getNodeAs("signed-operand"); assert(SignedOperand && "No signed operand found in problematic bitwise operations"); bool IsUnary = false; SourceLocation Location; if (const auto *UnaryOp = N.getNodeAs("unary-signed")) { IsUnary = true; Location = UnaryOp->getBeginLoc(); } else { if (const auto *BinaryOp = N.getNodeAs("binary-no-sign-interference")) Location = BinaryOp->getBeginLoc(); else if (const auto *BinaryOp = N.getNodeAs("binary-sign-interference")) Location = BinaryOp->getBeginLoc(); else llvm_unreachable("unexpected matcher result"); } diag(Location, "use of a signed integer operand with a " "%select{binary|unary}0 bitwise operator") << IsUnary << SignedOperand->getSourceRange(); } } // namespace hicpp } // namespace tidy } // namespace clang