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