xref: /llvm-project/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp (revision 10bdcf6b4cd37d017753b3821fbf8eb2ad924a1a)
1a8c34530SJonas Toth //===--- SignedBitwiseCheck.cpp - clang-tidy-------------------------------===//
2a8c34530SJonas Toth //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a8c34530SJonas Toth //
7a8c34530SJonas Toth //===----------------------------------------------------------------------===//
8a8c34530SJonas Toth 
9a8c34530SJonas Toth #include "SignedBitwiseCheck.h"
10a8c34530SJonas Toth #include "clang/AST/ASTContext.h"
11a8c34530SJonas Toth #include "clang/ASTMatchers/ASTMatchFinder.h"
12*10bdcf6bSPiotr Zegar #include "clang/ASTMatchers/ASTMatchers.h"
13a8c34530SJonas Toth 
14a8c34530SJonas Toth using namespace clang::ast_matchers;
15a8c34530SJonas Toth using namespace clang::ast_matchers::internal;
16a8c34530SJonas Toth 
177d2ea6c4SCarlos Galvez namespace clang::tidy::hicpp {
18a8c34530SJonas Toth 
SignedBitwiseCheck(StringRef Name,ClangTidyContext * Context)194de6b158SVladimir Plyashkun SignedBitwiseCheck::SignedBitwiseCheck(StringRef Name,
204de6b158SVladimir Plyashkun                                        ClangTidyContext *Context)
214de6b158SVladimir Plyashkun     : ClangTidyCheck(Name, Context),
224de6b158SVladimir Plyashkun       IgnorePositiveIntegerLiterals(
234de6b158SVladimir Plyashkun           Options.get("IgnorePositiveIntegerLiterals", false)) {}
244de6b158SVladimir Plyashkun 
storeOptions(ClangTidyOptions::OptionMap & Opts)254de6b158SVladimir Plyashkun void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
264de6b158SVladimir Plyashkun   Options.store(Opts, "IgnorePositiveIntegerLiterals",
274de6b158SVladimir Plyashkun                 IgnorePositiveIntegerLiterals);
284de6b158SVladimir Plyashkun }
294de6b158SVladimir Plyashkun 
registerMatchers(MatchFinder * Finder)30a8c34530SJonas Toth void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) {
31a8c34530SJonas Toth   const auto SignedIntegerOperand =
324de6b158SVladimir Plyashkun       (IgnorePositiveIntegerLiterals
33*10bdcf6bSPiotr Zegar            ? expr(ignoringImpCasts(
34*10bdcf6bSPiotr Zegar                  allOf(hasType(isSignedInteger()), unless(integerLiteral()))))
354de6b158SVladimir Plyashkun            : expr(ignoringImpCasts(hasType(isSignedInteger()))))
364de6b158SVladimir Plyashkun           .bind("signed-operand");
378ba28c72SJonas Toth 
388ba28c72SJonas Toth   // The standard [bitmask.types] allows some integral types to be implemented
398ba28c72SJonas Toth   // as signed types. Exclude these types from diagnosing for bitwise or(|) and
408ba28c72SJonas Toth   // bitwise and(&). Shifting and complementing such values is still not
418ba28c72SJonas Toth   // allowed.
42976e0c07SAlexander Kornienko   const auto BitmaskType = namedDecl(
43976e0c07SAlexander Kornienko       hasAnyName("::std::locale::category", "::std::ctype_base::mask",
44976e0c07SAlexander Kornienko                  "::std::ios_base::fmtflags", "::std::ios_base::iostate",
45976e0c07SAlexander Kornienko                  "::std::ios_base::openmode"));
468ba28c72SJonas Toth   const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType)));
47a8c34530SJonas Toth 
48a8c34530SJonas Toth   // Match binary bitwise operations on signed integer arguments.
49a8c34530SJonas Toth   Finder->addMatcher(
5097572fa6SNathan James       binaryOperator(hasAnyOperatorName("^", "|", "&", "^=", "|=", "&="),
518ba28c72SJonas Toth 
528ba28c72SJonas Toth                      unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))),
538ba28c72SJonas Toth 
548ba28c72SJonas Toth                      hasEitherOperand(SignedIntegerOperand),
55976e0c07SAlexander Kornienko                      hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))
568ba28c72SJonas Toth           .bind("binary-no-sign-interference"),
578ba28c72SJonas Toth       this);
588ba28c72SJonas Toth 
598ba28c72SJonas Toth   // Shifting and complement is not allowed for any signed integer type because
608ba28c72SJonas Toth   // the sign bit may corrupt the result.
618ba28c72SJonas Toth   Finder->addMatcher(
6297572fa6SNathan James       binaryOperator(hasAnyOperatorName("<<", ">>", "<<=", ">>="),
63c1f906c1SJonas Toth                      hasEitherOperand(SignedIntegerOperand),
64976e0c07SAlexander Kornienko                      hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))
658ba28c72SJonas Toth           .bind("binary-sign-interference"),
66a8c34530SJonas Toth       this);
67a8c34530SJonas Toth 
68a8c34530SJonas Toth   // Match unary operations on signed integer types.
69976e0c07SAlexander Kornienko   Finder->addMatcher(
70976e0c07SAlexander Kornienko       unaryOperator(hasOperatorName("~"), hasUnaryOperand(SignedIntegerOperand))
718ba28c72SJonas Toth           .bind("unary-signed"),
72a8c34530SJonas Toth       this);
73a8c34530SJonas Toth }
74a8c34530SJonas Toth 
check(const MatchFinder::MatchResult & Result)75a8c34530SJonas Toth void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) {
76a8c34530SJonas Toth   const ast_matchers::BoundNodes &N = Result.Nodes;
778ba28c72SJonas Toth   const auto *SignedOperand = N.getNodeAs<Expr>("signed-operand");
788ba28c72SJonas Toth   assert(SignedOperand &&
798ba28c72SJonas Toth          "No signed operand found in problematic bitwise operations");
80a8c34530SJonas Toth 
818ba28c72SJonas Toth   bool IsUnary = false;
82fa8f8616SVladimir Plyashkun   SourceLocation OperatorLoc;
838ba28c72SJonas Toth 
848ba28c72SJonas Toth   if (const auto *UnaryOp = N.getNodeAs<UnaryOperator>("unary-signed")) {
858ba28c72SJonas Toth     IsUnary = true;
86fa8f8616SVladimir Plyashkun     OperatorLoc = UnaryOp->getOperatorLoc();
878ba28c72SJonas Toth   } else {
888ba28c72SJonas Toth     if (const auto *BinaryOp =
898ba28c72SJonas Toth             N.getNodeAs<BinaryOperator>("binary-no-sign-interference"))
90fa8f8616SVladimir Plyashkun       OperatorLoc = BinaryOp->getOperatorLoc();
918ba28c72SJonas Toth     else if (const auto *BinaryOp =
928ba28c72SJonas Toth                  N.getNodeAs<BinaryOperator>("binary-sign-interference"))
93fa8f8616SVladimir Plyashkun       OperatorLoc = BinaryOp->getOperatorLoc();
948ba28c72SJonas Toth     else
958ba28c72SJonas Toth       llvm_unreachable("unexpected matcher result");
968ba28c72SJonas Toth   }
97fa8f8616SVladimir Plyashkun   diag(SignedOperand->getBeginLoc(), "use of a signed integer operand with a "
980f5f41dfSJonas Toth                                      "%select{binary|unary}0 bitwise operator")
99fa8f8616SVladimir Plyashkun       << IsUnary << SignedOperand->getSourceRange() << OperatorLoc;
100a8c34530SJonas Toth }
101a8c34530SJonas Toth 
1027d2ea6c4SCarlos Galvez } // namespace clang::tidy::hicpp
103