11#include "clang/AST/ASTContext.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/Expr.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/ASTMatchers/ASTMatchers.h"
16#include "clang/ASTMatchers/ASTMatchersInternal.h"
17#include "clang/Basic/Diagnostic.h"
18#include "clang/Basic/LLVM.h"
19#include "clang/Basic/SourceLocation.h"
20#include "clang/Basic/SourceManager.h"
21#include "clang/Lex/Lexer.h"
22#include "llvm/ADT/ArrayRef.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallBitVector.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/ADT/Twine.h"
28#include "llvm/Support/raw_ostream.h"
35static constexpr const char BoundCall[] =
"CallExpr";
36static constexpr const char FuncDecl[] =
"FuncDecl";
37static constexpr const char ArgName[] =
"ArgName";
41static std::string
getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) {
43 llvm::raw_string_ostream OS(Output);
45 OS << Item.BeginArg <<
":" << Item.EndArg <<
":"
52AST_MATCHER(Expr, hasSideEffects) {
53 return Node.HasSideEffects(Finder->getASTContext());
59 ArrayRef<StringRef> MethodNames,
60 ArrayRef<StringRef> FreeNames) {
62 anyOf(cxxMemberCallExpr(argumentCountIs(0),
63 callee(cxxMethodDecl(hasAnyName(MethodNames))),
65 callExpr(argumentCountIs(1), hasArgument(0, ArgumentMatcher),
66 hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
69static ast_matchers::internal::Matcher<CallExpr>
71 ArrayRef<StringRef> BeginFreeNames,
72 ArrayRef<StringRef> EndFreeNames,
73 const std::optional<UseRangesCheck::ReverseIteratorDescriptor>
76 const SmallString<64> ID = {
BoundCall, State};
77 ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
80 {
"begin",
"cbegin"}, BeginFreeNames)),
81 hasArgument(Indexes.
EndArg,
83 expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
84 {
"end",
"cend"}, EndFreeNames)));
85 if (ReverseDescriptor) {
86 ArgBound.push_back(
'R');
88 llvm::make_first_range(ReverseDescriptor->FreeReverseNames)};
90 llvm::make_second_range(ReverseDescriptor->FreeReverseNames)};
91 ArgumentMatcher = anyOf(
96 {
"rbegin",
"crbegin"}, RBegin)),
100 expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
101 {
"rend",
"crend"}, REnd))));
103 return callExpr(argumentCountAtLeast(
114 llvm::make_first_range(BeginEndNames)};
116 llvm::make_second_range(BeginEndNames)};
118 llvm::DenseSet<Replacer *> SeenRepl;
119 for (
auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
121 if (!SeenRepl.insert(
Replacer.get()).second)
126 [](
const Signature &Index) { return !Index.empty(); }));
127 std::vector<StringRef> Names(1, I->getKey());
128 for (
auto J = std::next(I); J != E; ++J)
130 Names.push_back(J->getKey());
132 std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers;
138 llvm::sort(SigVec, [](
auto &L,
auto &R) {
return R.size() < L.size(); });
140 std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
143 BeginNames, EndNames,
145 TotalMatchers.push_back(
146 ast_matchers::internal::DynTypedMatcher::constructVariadic(
147 ast_matchers::internal::DynTypedMatcher::VO_AllOf,
148 ASTNodeKind::getFromNodeKind<CallExpr>(), std::move(Matchers)));
152 callee(functionDecl(hasAnyName(Names))
153 .bind((
FuncDecl + Twine(Replacers.size() - 1).str()))),
154 ast_matchers::internal::DynTypedMatcher::constructVariadic(
155 ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
156 ASTNodeKind::getFromNodeKind<CallExpr>(),
157 std::move(TotalMatchers))
158 .convertTo<CallExpr>()),
164 const CallExpr &Call, ArrayRef<unsigned> Indexes,
165 const ASTContext &Ctx) {
169 llvm::SmallBitVector Commas(Call.getNumArgs());
172 for (
const unsigned Index : Sorted) {
173 const Expr *Arg = Call.getArg(Index);
175 if (Index >= Commas.size()) {
176 Diag << FixItHint::CreateRemoval(Arg->getSourceRange());
179 Commas[Index + 1] =
true;
180 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
182 Lexer::getLocForEndOfToken(
183 Arg->getEndLoc(), 0, Ctx.getSourceManager(), Ctx.getLangOpts())
184 .getLocWithOffset(1)}));
187 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
188 Arg->getBeginLoc().getLocWithOffset(-1), Arg->getEndLoc()));
189 Commas[Index] =
true;
195 const ast_matchers::MatchFinder::MatchResult &Result) {
196 const DynTypedNodeList Parents = Result.Context->getParents(Node);
197 assert(Parents.size() == 1 &&
198 "Expected exactly one parent for a matched algorithm call");
199 const DynTypedNode &Parent = Parents[0];
200 if (Parent.get<CompoundStmt>())
202 if (
const auto *Cleanups = Parent.get<ExprWithCleanups>())
203 return isResultUsed(DynTypedNode::create(*Cleanups), Result);
204 if (
const auto *Temporary = Parent.get<CXXBindTemporaryExpr>())
205 return isResultUsed(DynTypedNode::create(*Temporary), Result);
210 const ast_matchers::MatchFinder::MatchResult &Result) {
211 return isResultUsed(DynTypedNode::create(Call), Result);
215 StringRef Accessor,
const ASTContext &Ctx) {
216 const SourceLocation End = Lexer::getLocForEndOfToken(
217 Call.getEndLoc(), 0, Ctx.getSourceManager(), Ctx.getLangOpts());
219 Diag << FixItHint::CreateInsertion(End, Accessor);
224 const FunctionDecl *Function =
nullptr;
225 for (
const auto &[Node, Value] : Result.Nodes.getMap()) {
226 StringRef NodeStr(Node);
227 if (!NodeStr.consume_front(
FuncDecl))
229 Function = Value.get<FunctionDecl>();
231 if (NodeStr.getAsInteger(10, Index))
232 llvm_unreachable(
"Unable to extract replacer index");
233 assert(Index < Replacers.size());
238 SmallString<64> Buffer;
241 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(Buffer);
247 if (Function->getName() ==
"find") {
248 const unsigned ValueArgIndex = 2;
249 if (Call->getNumArgs() <= ValueArgIndex)
251 const Expr *ValueExpr =
252 Call->getArg(ValueArgIndex)->IgnoreParenImpCasts();
253 if (isa<CXXNullPtrLiteralExpr>(ValueExpr))
262 Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(),
265 Diag << Inserter.createIncludeInsertion(
266 Result.SourceManager->getFileID(Call->getBeginLoc()), *Include);
268 for (
const auto &[First, Second, Replace] : Sig) {
269 auto ArgNode =
ArgName + std::to_string(First);
270 if (
const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode)) {
271 Diag << FixItHint::CreateReplacement(
274 Lexer::getSourceText(
275 CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
276 Result.Context->getSourceManager(),
277 Result.Context->getLangOpts()));
279 assert(ReverseDescriptor &&
"Couldn't find forward argument");
280 ArgNode.push_back(
'R');
281 ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode);
282 assert(ArgExpr &&
"Couldn't find forward or reverse argument");
283 if (ReverseDescriptor->ReverseHeader)
284 Diag << Inserter.createIncludeInsertion(
285 Result.SourceManager->getFileID(Call->getBeginLoc()),
286 *ReverseDescriptor->ReverseHeader);
287 const StringRef ArgText = Lexer::getSourceText(
288 CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
289 Result.Context->getSourceManager(), Result.Context->getLangOpts());
290 SmallString<128> ReplaceText;
291 if (ReverseDescriptor->IsPipeSyntax)
293 {ArgText,
" | ", ReverseDescriptor->ReverseAdaptorName});
296 {ReverseDescriptor->ReverseAdaptorName,
"(", ArgText,
")"});
297 Diag << FixItHint::CreateReplacement(
306 if (ResultUsed && ResultPolicy.PolicyKind ==
307 ResultPolicyKind::AppendAccessorForUsedResult) {
308 insertAccessor(Diag, *Call, ResultPolicy.Accessor, *Result.Context);
312 llvm_unreachable(
"No valid signature found");
316 const LangOptions &LangOpts)
const {
317 return LangOpts.CPlusPlus11;
322 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
324 areDiagsSelfContained()) {}
327 Preprocessor *PP, Preprocessor *) {
328 Inserter.registerPreprocessor(PP);
332 Options.store(Opts,
"IncludeStyle", Inserter.getStyle());
335std::optional<std::string>
346 return diag(Call.getBeginLoc(),
"use a ranges version of this algorithm");
349std::optional<UseRangesCheck::ReverseIteratorDescriptor>
354ArrayRef<std::pair<StringRef, StringRef>>
360 return TK_IgnoreUnlessSpelledInSource;
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
virtual ArrayRef< Signature > getReplacementSignatures() const =0
Gets an array of all the possible overloads for a function with indexes where begin and end arguments...
virtual ResultUsePolicy getResultUsePolicy(const NamedDecl &OriginalName, bool IsStructuredBinding) const
Gets how to handle fix-its when the original algorithm result is used.
virtual std::optional< std::string > getHeaderInclusion(const NamedDecl &OriginalName) const
Gets the header needed to access the replaced function Return std::nullopt if no new header is needed...
virtual std::optional< std::string > getReplaceName(const NamedDecl &OriginalName) const =0
Gets the name to replace a function with, return std::nullopt for a replacement where we just call a ...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
virtual DiagnosticBuilder createDiag(const CallExpr &Call)
Create a diagnostic for the CallExpr Override this to support custom diagnostic messages.
void registerMatchers(ast_matchers::MatchFinder *Finder) final
std::optional< TraversalKind > getCheckTraversalKind() const override
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) final
virtual ReplacerMap getReplacerMap() const =0
Gets a map of function to replace and methods to create the replacements.
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
UseRangesCheck(StringRef Name, ClangTidyContext *Context)
virtual std::optional< ReverseIteratorDescriptor > getReverseDescriptor() const
virtual ArrayRef< std::pair< StringRef, StringRef > > getFreeBeginEndMethods() const
Gets the fully qualified names of begin and end functions.
SmallVector< Indexes, 2 > Signature
static std::string getFullPrefix(ArrayRef< UseRangesCheck::Indexes > Signature)
static ast_matchers::internal::Matcher< CallExpr > makeMatcherPair(StringRef State, const UseRangesCheck::Indexes &Indexes, ArrayRef< StringRef > BeginFreeNames, ArrayRef< StringRef > EndFreeNames, const std::optional< UseRangesCheck::ReverseIteratorDescriptor > &ReverseDescriptor)
static bool isResultUsed(const DynTypedNode &Node, const ast_matchers::MatchFinder::MatchResult &Result)
IncludeSorter(const SourceManager *SourceMgr, FileID FileID, StringRef FileName, IncludeStyle Style)
Class used by IncludeInserterCallback to record the names of the / inclusions in a given source file ...
static void insertAccessor(const DiagnosticBuilder &Diag, const CallExpr &Call, StringRef Accessor, const ASTContext &Ctx)
static auto makeExprMatcher(const ast_matchers::internal::Matcher< Expr > &ArgumentMatcher, ArrayRef< StringRef > MethodNames, ArrayRef< StringRef > FreeNames)
static void removeFunctionArgs(const DiagnosticBuilder &Diag, const CallExpr &Call, ArrayRef< unsigned > Indexes, const ASTContext &Ctx)
llvm::StringMap< ClangTidyValue > OptionMap
static constexpr const char ArgName[]
static constexpr const char BoundCall[]
static constexpr const char FuncDecl[]