Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 1 | //===--- SignedBitwiseCheck.cpp - clang-tidy-------------------------------===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 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 { |
| 17 | namespace tidy { |
| 18 | namespace hicpp { |
| 19 | |
Vladimir Plyashkun | 4de6b15 | 2019-10-30 14:07:49 -0400 | [diff] [blame] | 20 | SignedBitwiseCheck::SignedBitwiseCheck(StringRef Name, |
| 21 | ClangTidyContext *Context) |
| 22 | : ClangTidyCheck(Name, Context), |
| 23 | IgnorePositiveIntegerLiterals( |
| 24 | Options.get("IgnorePositiveIntegerLiterals", false)) {} |
| 25 | |
| 26 | void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
| 27 | Options.store(Opts, "IgnorePositiveIntegerLiterals", |
| 28 | IgnorePositiveIntegerLiterals); |
| 29 | } |
| 30 | |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 31 | void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { |
| 32 | const auto SignedIntegerOperand = |
Vladimir Plyashkun | 4de6b15 | 2019-10-30 14:07:49 -0400 | [diff] [blame] | 33 | (IgnorePositiveIntegerLiterals |
| 34 | ? expr(ignoringImpCasts(hasType(isSignedInteger())), |
| 35 | unless(integerLiteral())) |
| 36 | : expr(ignoringImpCasts(hasType(isSignedInteger())))) |
| 37 | .bind("signed-operand"); |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 38 | |
| 39 | // The standard [bitmask.types] allows some integral types to be implemented |
| 40 | // as signed types. Exclude these types from diagnosing for bitwise or(|) and |
| 41 | // bitwise and(&). Shifting and complementing such values is still not |
| 42 | // allowed. |
Alexander Kornienko | 976e0c0 | 2018-11-25 02:41:01 +0000 | [diff] [blame] | 43 | const auto BitmaskType = namedDecl( |
| 44 | hasAnyName("::std::locale::category", "::std::ctype_base::mask", |
| 45 | "::std::ios_base::fmtflags", "::std::ios_base::iostate", |
| 46 | "::std::ios_base::openmode")); |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 47 | const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType))); |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 48 | |
| 49 | // Match binary bitwise operations on signed integer arguments. |
| 50 | Finder->addMatcher( |
Nathan James | 97572fa | 2020-03-10 00:42:21 +0000 | [diff] [blame] | 51 | binaryOperator(hasAnyOperatorName("^", "|", "&", "^=", "|=", "&="), |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 52 | |
Alexander Kornienko | 976e0c0 | 2018-11-25 02:41:01 +0000 | [diff] [blame] | 53 | unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))), |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 54 | |
Alexander Kornienko | 976e0c0 | 2018-11-25 02:41:01 +0000 | [diff] [blame] | 55 | hasEitherOperand(SignedIntegerOperand), |
| 56 | hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))) |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 57 | .bind("binary-no-sign-interference"), |
| 58 | this); |
| 59 | |
| 60 | // Shifting and complement is not allowed for any signed integer type because |
| 61 | // the sign bit may corrupt the result. |
| 62 | Finder->addMatcher( |
Nathan James | 97572fa | 2020-03-10 00:42:21 +0000 | [diff] [blame] | 63 | binaryOperator(hasAnyOperatorName("<<", ">>", "<<=", ">>="), |
Alexander Kornienko | 976e0c0 | 2018-11-25 02:41:01 +0000 | [diff] [blame] | 64 | hasEitherOperand(SignedIntegerOperand), |
| 65 | hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))) |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 66 | .bind("binary-sign-interference"), |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 67 | this); |
| 68 | |
| 69 | // Match unary operations on signed integer types. |
Alexander Kornienko | 976e0c0 | 2018-11-25 02:41:01 +0000 | [diff] [blame] | 70 | Finder->addMatcher( |
| 71 | unaryOperator(hasOperatorName("~"), hasUnaryOperand(SignedIntegerOperand)) |
| 72 | .bind("unary-signed"), |
| 73 | this); |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { |
| 77 | const ast_matchers::BoundNodes &N = Result.Nodes; |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 78 | const auto *SignedOperand = N.getNodeAs<Expr>("signed-operand"); |
| 79 | assert(SignedOperand && |
| 80 | "No signed operand found in problematic bitwise operations"); |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 81 | |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 82 | bool IsUnary = false; |
| 83 | SourceLocation Location; |
| 84 | |
| 85 | if (const auto *UnaryOp = N.getNodeAs<UnaryOperator>("unary-signed")) { |
| 86 | IsUnary = true; |
Stephen Kelly | 43465bf | 2018-08-09 22:42:26 +0000 | [diff] [blame] | 87 | Location = UnaryOp->getBeginLoc(); |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 88 | } else { |
| 89 | if (const auto *BinaryOp = |
| 90 | N.getNodeAs<BinaryOperator>("binary-no-sign-interference")) |
Stephen Kelly | 43465bf | 2018-08-09 22:42:26 +0000 | [diff] [blame] | 91 | Location = BinaryOp->getBeginLoc(); |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 92 | else if (const auto *BinaryOp = |
| 93 | N.getNodeAs<BinaryOperator>("binary-sign-interference")) |
Stephen Kelly | 43465bf | 2018-08-09 22:42:26 +0000 | [diff] [blame] | 94 | Location = BinaryOp->getBeginLoc(); |
Jonas Toth | 8ba28c7 | 2017-10-27 14:44:08 +0000 | [diff] [blame] | 95 | else |
| 96 | llvm_unreachable("unexpected matcher result"); |
| 97 | } |
Jonas Toth | 0f5f41d | 2018-04-11 09:53:08 +0000 | [diff] [blame] | 98 | diag(Location, "use of a signed integer operand with a " |
| 99 | "%select{binary|unary}0 bitwise operator") |
Jonas Toth | a8c3453 | 2017-08-30 13:32:05 +0000 | [diff] [blame] | 100 | << IsUnary << SignedOperand->getSourceRange(); |
| 101 | } |
| 102 | |
| 103 | } // namespace hicpp |
| 104 | } // namespace tidy |
| 105 | } // namespace clang |