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