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