================ @@ -0,0 +1,392 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "BoolBitwiseOperationCheck.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/DynamicRecursiveASTVisitor.h" +#include "clang/AST/Expr.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Lexer.h" +#include <array> +#include <type_traits> +#include <utility> + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +static constexpr std::array<std::pair<StringRef, StringRef>, 8U> + OperatorsTransformation{{{"|", "||"}, + {"|=", "||"}, + {"&", "&&"}, + {"&=", "&&"}, + {"bitand", "and"}, + {"and_eq", "and"}, + {"bitor", "or"}, + {"or_eq", "or"}}}; + +static constexpr std::integral_constant<bool, true> RespectStrictMode{}; +static constexpr std::integral_constant<bool, false> IgnoreStrictMode{}; + +static StringRef translate(StringRef Value) { + for (const auto &[Bitwise, Logical] : OperatorsTransformation) + if (Value == Bitwise) + return Logical; + + return {}; +} + +static bool isBitwiseOperation(StringRef Value) { + return llvm::is_contained(llvm::make_first_range(OperatorsTransformation), + Value); +} + +static std::optional<CharSourceRange> +getOperatorTokenRangeForFixIt(const BinaryOperator *BinOp, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Loc = BinOp->getOperatorLoc(); + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + Loc = SM.getSpellingLoc(Loc); + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc); + if (TokenRange.isInvalid()) + return std::nullopt; + + return TokenRange; +} + +static std::optional<SourceLocation> +getSpellingLocationForFixIt(SourceLocation Loc, const SourceManager &SM) { + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + Loc = SM.getSpellingLoc(Loc); + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + return Loc; +} + +static std::optional<SourceLocation> +getEndOfTokenLocationForFixIt(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts) { + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + Loc = SM.getSpellingLoc(Loc); + if (Loc.isInvalid() || Loc.isMacroID()) + return std::nullopt; + + SourceLocation EndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); + if (EndLoc.isInvalid() || EndLoc.isMacroID()) + return std::nullopt; + + return EndLoc; +} + +/// Checks if all leaf nodes in a bitwise expression satisfy a given condition. +/// +/// \param Expr The bitwise expression to check. +/// \param Condition A function that checks if a leaf node satisfies the +/// desired condition. +/// \returns true if the condition is satisfied according to the combiner logic. +template <typename F> +static bool leavesOfBitwiseSatisfy(const clang::Expr *Expr, + const F &Condition) { + // Strip away implicit casts and parentheses before checking the condition. + // This is important for cases like: + // bool b1, b2; + // bool Deprecated = 0xFFFFFFF8 & (b1 & b2); + // where the operands of the inner '&' are represented in the AST as + // ImplicitCastExpr <int> (ImplicitCastExpr <bool> (DeclRefExpr 'bool')) + // and we still want to classify the leaves as boolean. + Expr = Expr->IgnoreParenImpCasts(); + + // For leaf nodes, check if the condition is satisfied after stripping + // implicit casts/parens. + if (Condition(Expr)) + return true; + + // If it's a binary operator, recursively check both operands. + if (const auto *BinOp = dyn_cast<clang::BinaryOperator>(Expr)) { + if (!isBitwiseOperation(BinOp->getOpcodeStr())) + return false; + return leavesOfBitwiseSatisfy(BinOp->getLHS(), Condition) && + leavesOfBitwiseSatisfy(BinOp->getRHS(), Condition); + } + + return false; +} + +namespace { + +// FIXME: provide memoization for this matcher + +/// Custom matcher that checks if all leaf nodes in an bitwise expression +/// satisfy the given inner matcher condition. This uses +/// leavesOfBitwiseSatisfy to recursively check. +/// +/// Example usage: +/// expr(hasAllLeavesOfBitwiseSatisfying(hasType(booleanType()))) +AST_MATCHER_P(Expr, hasAllLeavesOfBitwiseSatisfying, + ast_matchers::internal::Matcher<Expr>, InnerMatcher) { + auto Condition = [&](const clang::Expr *E) -> bool { + return InnerMatcher.matches(*E, Finder, Builder); + }; + return leavesOfBitwiseSatisfy(&Node, Condition); +} + +AST_MATCHER_P(Expr, hasSideEffects, bool, IncludePossibleEffects) { + auto &Ctx = Finder->getASTContext(); + return Node.HasSideEffects(Ctx, IncludePossibleEffects); +} + +} // namespace + +BoolBitwiseOperationCheck::BoolBitwiseOperationCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + UnsafeMode(Options.get("UnsafeMode", false)), + IgnoreMacros(Options.get("IgnoreMacros", false)), + StrictMode(Options.get("StrictMode", true)), + BraceCompound(Options.get("BraceCompound", true)), + // Undocumented option for debugging purposes + IgnoreWarningsWithFixIt(Options.get("IgnoreWarningsWithFixIt", false)) {} ---------------- vbvictor wrote:
Should be removed, it will be present in `--dump-config` https://github.com/llvm/llvm-project/pull/167552 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
