================ @@ -0,0 +1,280 @@ +//===----------------------------------------------------------------------===// +// +// 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 "MissingEndComparisonCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +namespace { + +constexpr llvm::StringRef IteratorAlgorithms[] = { + "::std::find", "::std::find_if", + "::std::find_if_not", "::std::search", + "::std::search_n", "::std::find_end", + "::std::find_first_of", "::std::lower_bound", + "::std::upper_bound", "::std::partition_point", + "::std::min_element", "::std::max_element", + "::std::adjacent_find", "::std::is_sorted_until"}; + +constexpr llvm::StringRef RangeAlgorithms[] = { + "::std::ranges::find", "::std::ranges::find_if", + "::std::ranges::find_if_not", "::std::ranges::lower_bound", + "::std::ranges::upper_bound", "::std::ranges::min_element", + "::std::ranges::max_element", "::std::ranges::find_first_of", + "::std::ranges::adjacent_find", "::std::ranges::is_sorted_until"}; +} // namespace + +MissingEndComparisonCheck::MissingEndComparisonCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + ExtraAlgorithms( + utils::options::parseStringList(Options.get("ExtraAlgorithms", ""))) { +} + +void MissingEndComparisonCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ExtraAlgorithms", + utils::options::serializeStringList(ExtraAlgorithms)); +} + +static bool isConditionVar(const DeclStmt *S, ASTContext &Ctx) { + const auto &Parents = Ctx.getParents(*S); + if (Parents.empty()) + return false; + + const auto *ParentStmt = Parents[0].get<Stmt>(); + if (!ParentStmt) + return false; + + if (const auto *If = dyn_cast<IfStmt>(ParentStmt)) + return If->getConditionVariableDeclStmt() == S; + if (const auto *While = dyn_cast<WhileStmt>(ParentStmt)) + return While->getConditionVariableDeclStmt() == S; + if (const auto *For = dyn_cast<ForStmt>(ParentStmt)) + return For->getConditionVariableDeclStmt() == S; + + return false; +} + +static std::optional<std::string> +getRangesEndText(const MatchFinder::MatchResult &Result, const CallExpr *Call) { + const FunctionDecl *Callee = Call->getDirectCallee(); + assert(Callee && Callee->getNumParams() > 0 && + "Matcher should ensure Callee has parameters"); + + // Range overloads take a reference (R&&), Iterator overloads pass by value. + const bool IsIterPair = + !Callee->getParamDecl(0)->getType()->isReferenceType(); + + if (IsIterPair) { + if (Call->getNumArgs() < 3) + return std::nullopt; + // find(CPO, Iter, Sent, Val...) -> Sent is Arg 2. + const Expr *EndArg = Call->getArg(2); + return tooling::fixit::getText(*EndArg, *Result.Context).str(); + } + + if (Call->getNumArgs() < 2) + return std::nullopt; + // find(CPO, Range, Val, Proj) -> Range is Arg 1. + const Expr *RangeArg = Call->getArg(1); + // Avoid potential side-effects + const Expr *InnerRange = RangeArg->IgnoreParenImpCasts(); + if (isa<DeclRefExpr>(InnerRange) || isa<MemberExpr>(InnerRange)) { + const StringRef RangeText = + tooling::fixit::getText(*RangeArg, *Result.Context); + if (!RangeText.empty()) + return ("std::ranges::end(" + RangeText + ")").str(); + } + return ""; +} + +static std::optional<std::string> +getStandardEndText(const MatchFinder::MatchResult &Result, + const CallExpr *Call) { + if (Call->getNumArgs() < 2) + return std::nullopt; + + // Heuristic: if the first argument is a record type and the types of the ---------------- zeyi2 wrote:
This heuristic method is used to provide accurate fix-its for custom algorithms. An example: `if (llvm::find(my_vector, 42))` would be changed to `if ((llvm::find(my_vector, 42) != 42))` without this, which is a type error (comparing an iterator to an integer). Currently we check if a 2-argument call has a record type as its first argument and a different type for its second. https://github.com/llvm/llvm-project/pull/182543 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
