================ @@ -0,0 +1,176 @@ +//===----------------------------------------------------------------------===// +// +// 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 "NumericlimitmaxcheckCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +NumericlimitmaxcheckCheck::NumericlimitmaxcheckCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Inserter(Options.getLocalOrGlobal("IncludeStyle", utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void NumericlimitmaxcheckCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + Inserter.registerPreprocessor(PP); +} + +void NumericlimitmaxcheckCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", Inserter.getStyle()); +} + +bool NumericlimitmaxcheckCheck::isLanguageVersionSupported(const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus; +} + +void NumericlimitmaxcheckCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + auto NegOneLiteral = integerLiteral(equals(-1)); + auto ZeroLiteral = integerLiteral(equals(0)); + + auto NegOneExpr = anyOf( + NegOneLiteral, + unaryOperator(hasOperatorName("-"), + hasUnaryOperand(integerLiteral(equals(1))))); + + auto BitNotZero = unaryOperator(hasOperatorName("~"), + hasUnaryOperand(ZeroLiteral)); + + // Match implicit cast of -1 to unsigned + auto ImplicitNegOneToUnsigned = + implicitCastExpr( + hasSourceExpression(ignoringParenImpCasts(anyOf(NegOneExpr, BitNotZero))), + hasType(isUnsignedInteger())); + + // Match explicit cast to unsigned of either -1 or ~0 + auto ExplicitCastOfNegOrBitnot = + explicitCastExpr( + hasDestinationType(isUnsignedInteger()), + hasSourceExpression(ignoringParenImpCasts(anyOf(NegOneExpr, BitNotZero)))); + + // Match ~0 with unsigned type + auto UnsignedBitNotZero = + unaryOperator( + hasOperatorName("~"), + hasUnaryOperand(ZeroLiteral), + hasType(isUnsignedInteger())); + + auto UnsignedLiteralNegOne = + integerLiteral(equals(-1), hasType(isUnsignedInteger())); + + // To handle ternary branch case + auto TernaryBranch = + expr(anyOf(NegOneExpr, BitNotZero), + hasAncestor(conditionalOperator( + hasAncestor(implicitCastExpr(hasType(isUnsignedInteger() + )) + .bind("outerCast"))))) + .bind("unsignedMaxExpr"); + + auto Combined = + expr(anyOf( + ExplicitCastOfNegOrBitnot, + ImplicitNegOneToUnsigned, + UnsignedBitNotZero, + UnsignedLiteralNegOne + )).bind("unsignedMaxExpr"); + + Finder->addMatcher(Combined, this); + Finder->addMatcher(TernaryBranch, this); +} + + +void NumericlimitmaxcheckCheck::check(const MatchFinder::MatchResult &Result) { + const auto *E = Result.Nodes.getNodeAs<Expr>("unsignedMaxExpr"); + const auto *OuterCast = Result.Nodes.getNodeAs<CastExpr>("outerCast"); + + if (!E) + return; + + ASTContext &Ctx = *Result.Context; // Get context before first use + + QualType QT; + if (OuterCast) { + // This is ternary matcher. Get type from the cast. + QT = OuterCast->getType(); + } else { + // Get type from the bound expression. + QT = E->getType(); + } + + if (E->getBeginLoc().isInvalid() || E->getBeginLoc().isMacroID()) + return; + + const SourceManager &SM = Ctx.getSourceManager(); + + if (!OuterCast) { + auto Parents = Ctx.getParents(*E); + if (!Parents.empty()) { + for (const auto &Parent : Parents) { + // Check if parent is an explicit cast to unsigned + if (const auto *ParentCast = Parent.get<ExplicitCastExpr>()) { + if (ParentCast->getType()->isUnsignedIntegerType()) { + // Skip this match, avoids double reporting + return; + } + } + // Also check if parent is an implicit cast that's part of an explicit cast chain + if (const auto *ImplicitCast = Parent.get<ImplicitCastExpr>()) { + auto GrandParents = Ctx.getParents(*ImplicitCast); + for (const auto &GP : GrandParents) { + if (const auto *GPCast = GP.get<ExplicitCastExpr>()) { + if (GPCast->getType()->isUnsignedIntegerType()) { + return; + } + } + } + } + } + } + } + + if (QT.isNull() || !QT->isUnsignedIntegerType()) + return; + + std::string TypeStr = QT.getUnqualifiedType().getAsString(); + if (const auto *Typedef = QT->getAs<TypedefType>()) { + TypeStr = Typedef->getDecl()->getName().str(); + } + + // Get original source text for diagnostic message + StringRef OriginalText = + Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()), + SM, getLangOpts()); + + // Suggestion to the user regarding the usage of unsigned ~0 or -1 + std::string Replacement = "std::numeric_limits<" + TypeStr + ">::max()"; ---------------- EugeneZelenko wrote:
```suggestion const std::string Replacement = "std::numeric_limits<" + TypeStr + ">::max()"; ``` https://github.com/llvm/llvm-project/pull/167148 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
