10#include "../utils/ASTUtils.h"
11#include "../utils/LexerUtils.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Lex/Lexer.h"
20struct FindArgsResult {
24 SmallVector<const clang::Expr *, 2>
Args;
33static FindArgsResult
findArgs(
const CallExpr *Call) {
34 FindArgsResult Result;
35 Result.First =
nullptr;
36 Result.Last =
nullptr;
37 Result.Compare =
nullptr;
40 if (Call->getNumArgs() < 3) {
41 auto ArgIterator = Call->arguments().begin();
43 const auto *InitListExpr =
44 dyn_cast<CXXStdInitializerListExpr>(*ArgIterator);
45 const auto *InitList =
46 InitListExpr !=
nullptr
47 ? dyn_cast<clang::InitListExpr>(
48 InitListExpr->getSubExpr()->IgnoreImplicit())
52 Result.Args.append(InitList->inits().begin(), InitList->inits().end());
53 Result.First = *ArgIterator;
54 Result.Last = *ArgIterator;
57 std::advance(ArgIterator, 1);
58 if (ArgIterator != Call->arguments().end())
59 Result.Compare = *ArgIterator;
63 Result.Args = SmallVector<const Expr *>(Call->arguments());
66 Result.Compare = *(std::next(Call->arguments().begin(), 2));
67 Result.Args = SmallVector<const Expr *>(llvm::drop_end(Call->arguments()));
69 Result.First = Result.Args.front();
70 Result.Last = Result.Args.back();
79static std::pair<bool, SmallVector<FixItHint>>
81 const CallExpr *TopCall,
const FindArgsResult &Result,
82 const bool IgnoreNonTrivialTypes,
83 const std::uint64_t IgnoreTrivialTypesOfSizeAbove) {
84 SmallVector<FixItHint> FixItHints;
85 const SourceManager &SourceMngr = *Match.SourceManager;
86 const LangOptions &LanguageOpts = Match.Context->getLangOpts();
88 const QualType ResultType = TopCall->getDirectCallee()
91 .getNonReferenceType()
92 .getUnqualifiedType();
95 const bool IsResultTypeTrivial = ResultType.isTrivialType(*Match.Context);
97 if ((!IsResultTypeTrivial && IgnoreNonTrivialTypes))
98 return {
false, FixItHints};
100 if (IsResultTypeTrivial &&
101 static_cast<std::uint64_t
>(
102 Match.Context->getTypeSizeInChars(ResultType).getQuantity()) >
103 IgnoreTrivialTypesOfSizeAbove)
104 return {
false, FixItHints};
106 bool FoundNestedCall =
false;
108 for (
const Expr *Arg : Result.Args) {
109 const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());
114 const QualType ArgType = Arg->IgnoreParenImpCasts()
117 .getUnqualifiedType();
119 if (ArgType == ResultType)
122 const StringRef ArgText = Lexer::getSourceText(
123 CharSourceRange::getTokenRange(Arg->getSourceRange()), SourceMngr,
126 const auto Replacement = Twine(
"static_cast<")
127 .concat(ResultType.getAsString(LanguageOpts))
133 FixItHints.push_back(
134 FixItHint::CreateReplacement(Arg->getSourceRange(), Replacement));
139 if (InnerCall->getDirectCallee()->getQualifiedNameAsString() !=
140 TopCall->getDirectCallee()->getQualifiedNameAsString())
143 const FindArgsResult InnerResult =
findArgs(InnerCall);
146 if (!InnerResult.First || !InnerResult.Last)
150 if ((Result.Compare || InnerResult.Compare) &&
156 FoundNestedCall =
true;
159 FixItHints.push_back(
160 FixItHint::CreateRemoval(InnerCall->getCallee()->getSourceRange()));
164 InnerCall->getCallee()->getEndLoc(), SourceMngr, LanguageOpts);
165 if (LParen.has_value() && LParen->is(tok::l_paren))
166 FixItHints.push_back(
167 FixItHint::CreateRemoval(SourceRange(LParen->getLocation())));
168 FixItHints.push_back(
169 FixItHint::CreateRemoval(SourceRange(InnerCall->getRParenLoc())));
172 if (InnerResult.First == InnerResult.Last) {
174 FixItHints.push_back(FixItHint::CreateRemoval(
175 CharSourceRange::getTokenRange(InnerResult.First->getBeginLoc())));
176 FixItHints.push_back(FixItHint::CreateRemoval(
177 CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
181 Match, InnerCall, InnerResult, IgnoreNonTrivialTypes,
182 IgnoreTrivialTypesOfSizeAbove);
184 FixItHints.append(InnerReplacements);
186 if (InnerResult.Compare) {
189 InnerResult.Last->getEndLoc(), SourceMngr, LanguageOpts);
192 if (Comma.has_value() && Comma->is(tok::comma))
193 FixItHints.push_back(
194 FixItHint::CreateRemoval(SourceRange(Comma->getLocation())));
196 FixItHints.push_back(
197 FixItHint::CreateRemoval(InnerResult.Compare->getSourceRange()));
201 return {FoundNestedCall, FixItHints};
207 IgnoreNonTrivialTypes(Options.get(
"IgnoreNonTrivialTypes", true)),
208 IgnoreTrivialTypesOfSizeAbove(
209 Options.get(
"IgnoreTrivialTypesOfSizeAbove", 32L)),
210 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
211 utils::IncludeSorter::IS_LLVM),
212 areDiagsSelfContained()) {}
216 Options.
store(Opts,
"IgnoreNonTrivialTypes", IgnoreNonTrivialTypes);
218 IgnoreTrivialTypesOfSizeAbove);
223 auto CreateMatcher = [](
const StringRef FunctionName) {
224 auto FuncDecl = functionDecl(hasName(FunctionName));
225 auto Expression = callExpr(callee(
FuncDecl));
228 anyOf(hasArgument(0, Expression),
229 hasArgument(1, Expression),
230 hasArgument(0, cxxStdInitializerListExpr())),
231 unless(hasParent(Expression)))
235 Finder->addMatcher(CreateMatcher(
"::std::max"),
this);
236 Finder->addMatcher(CreateMatcher(
"::std::min"),
this);
240 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
245 const MatchFinder::MatchResult &Match) {
247 const auto *TopCall = Match.Nodes.getNodeAs<CallExpr>(
"topCall");
249 const FindArgsResult Result =
findArgs(TopCall);
250 const auto [FoundNestedCall, Replacements] =
252 IgnoreTrivialTypesOfSizeAbove);
254 if (!FoundNestedCall)
258 diag(TopCall->getBeginLoc(),
259 "do not use nested 'std::%0' calls, use an initializer list instead")
260 << TopCall->getDirectCallee()->getName()
262 Match.SourceManager->getFileID(TopCall->getBeginLoc()),
266 if (Result.First != Result.Last) {
268 Diagnostic << FixItHint::CreateInsertion(Result.First->getBeginLoc(),
"{");
271 Lexer::getLocForEndOfToken(Result.Last->getEndLoc(), 0,
272 *Match.SourceManager,
273 Match.Context->getLangOpts()),
llvm::SmallString< 256U > Name
DiagnosticCallback Diagnostic
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
MinMaxUseInitializerListCheck(StringRef Name, ClangTidyContext *Context)
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void check(const ast_matchers::MatchFinder::MatchResult &Match) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
static FindArgsResult findArgs(const CallExpr *Call)
static std::pair< bool, SmallVector< FixItHint > > generateReplacements(const MatchFinder::MatchResult &Match, const CallExpr *TopCall, const FindArgsResult &Result, const bool IgnoreNonTrivialTypes, const std::uint64_t IgnoreTrivialTypesOfSizeAbove)
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt, const ASTContext &Context, bool Canonical)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< ClangTidyValue > OptionMap
static constexpr const char FuncDecl[]