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 | #include "clang/ASTMatchers/ASTMatchers.h" |
13 | |
14 | using namespace clang::ast_matchers; |
15 | using namespace clang::ast_matchers::internal; |
16 | |
17 | namespace clang::tidy::hicpp { |
18 | |
19 | SignedBitwiseCheck::SignedBitwiseCheck(StringRef Name, |
20 | ClangTidyContext *Context) |
21 | : ClangTidyCheck(Name, Context), |
22 | IgnorePositiveIntegerLiterals( |
23 | Options.get(LocalName: "IgnorePositiveIntegerLiterals" , Default: false)) {} |
24 | |
25 | void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
26 | Options.store(Options&: Opts, LocalName: "IgnorePositiveIntegerLiterals" , |
27 | Value: IgnorePositiveIntegerLiterals); |
28 | } |
29 | |
30 | void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { |
31 | const auto SignedIntegerOperand = |
32 | (IgnorePositiveIntegerLiterals |
33 | ? expr(ignoringImpCasts( |
34 | InnerMatcher: allOf(hasType(InnerMatcher: isSignedInteger()), unless(integerLiteral())))) |
35 | : expr(ignoringImpCasts(InnerMatcher: hasType(InnerMatcher: isSignedInteger())))) |
36 | .bind(ID: "signed-operand" ); |
37 | |
38 | // The standard [bitmask.types] allows some integral types to be implemented |
39 | // as signed types. Exclude these types from diagnosing for bitwise or(|) and |
40 | // bitwise and(&). Shifting and complementing such values is still not |
41 | // 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(InnerMatcher: declRefExpr(hasType(InnerMatcher: BitmaskType))); |
47 | |
48 | // Match binary bitwise operations on signed integer arguments. |
49 | Finder->addMatcher( |
50 | NodeMatch: binaryOperator(hasAnyOperatorName("^" , "|" , "&" , "^=" , "|=" , "&=" ), |
51 | |
52 | unless(allOf(hasLHS(InnerMatcher: IsStdBitmask), hasRHS(InnerMatcher: IsStdBitmask))), |
53 | |
54 | hasEitherOperand(InnerMatcher: SignedIntegerOperand), |
55 | hasLHS(InnerMatcher: hasType(InnerMatcher: isInteger())), hasRHS(InnerMatcher: hasType(InnerMatcher: isInteger()))) |
56 | .bind(ID: "binary-no-sign-interference" ), |
57 | Action: this); |
58 | |
59 | // Shifting and complement is not allowed for any signed integer type because |
60 | // the sign bit may corrupt the result. |
61 | Finder->addMatcher( |
62 | NodeMatch: binaryOperator(hasAnyOperatorName("<<" , ">>" , "<<=" , ">>=" ), |
63 | hasEitherOperand(InnerMatcher: SignedIntegerOperand), |
64 | hasLHS(InnerMatcher: hasType(InnerMatcher: isInteger())), hasRHS(InnerMatcher: hasType(InnerMatcher: isInteger()))) |
65 | .bind(ID: "binary-sign-interference" ), |
66 | Action: this); |
67 | |
68 | // Match unary operations on signed integer types. |
69 | Finder->addMatcher( |
70 | NodeMatch: unaryOperator(hasOperatorName(Name: "~" ), hasUnaryOperand(InnerMatcher: SignedIntegerOperand)) |
71 | .bind(ID: "unary-signed" ), |
72 | Action: this); |
73 | } |
74 | |
75 | void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { |
76 | const ast_matchers::BoundNodes &N = Result.Nodes; |
77 | const auto *SignedOperand = N.getNodeAs<Expr>(ID: "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>(ID: "unary-signed" )) { |
85 | IsUnary = true; |
86 | OperatorLoc = UnaryOp->getOperatorLoc(); |
87 | } else { |
88 | if (const auto *BinaryOp = |
89 | N.getNodeAs<BinaryOperator>(ID: "binary-no-sign-interference" )) |
90 | OperatorLoc = BinaryOp->getOperatorLoc(); |
91 | else if (const auto *BinaryOp = |
92 | N.getNodeAs<BinaryOperator>(ID: "binary-sign-interference" )) |
93 | OperatorLoc = BinaryOp->getOperatorLoc(); |
94 | else |
95 | 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::hicpp |
103 | |