31static FindArgsResult
findArgs(
const CallExpr *Call) {
32 FindArgsResult Result;
33 Result.First =
nullptr;
34 Result.Last =
nullptr;
35 Result.Compare =
nullptr;
38 if (Call->getNumArgs() < 3) {
39 auto ArgIterator = Call->arguments().begin();
41 const auto *InitListExpr =
42 dyn_cast<CXXStdInitializerListExpr>(*ArgIterator);
43 const auto *InitList =
44 InitListExpr !=
nullptr
45 ? dyn_cast<clang::InitListExpr>(
46 InitListExpr->getSubExpr()->IgnoreImplicit())
50 Result.Args.append(InitList->inits().begin(), InitList->inits().end());
51 Result.First = *ArgIterator;
52 Result.Last = *ArgIterator;
55 std::advance(ArgIterator, 1);
56 if (ArgIterator != Call->arguments().end())
57 Result.Compare = *ArgIterator;
64 Result.Compare = *(std::next(Call->arguments().begin(), 2));
67 Result.First = Result.Args.front();
68 Result.Last = Result.Args.back();
79 const CallExpr *TopCall,
const FindArgsResult &Result,
80 const bool IgnoreNonTrivialTypes,
81 const std::uint64_t IgnoreTrivialTypesOfSizeAbove) {
83 const SourceManager &SourceMngr = *Match.SourceManager;
84 const LangOptions &LanguageOpts = Match.Context->getLangOpts();
86 const QualType ResultType = TopCall->getDirectCallee()
89 .getNonReferenceType()
90 .getUnqualifiedType();
93 const bool IsResultTypeTrivial = ResultType.isTrivialType(*Match.Context);
95 if ((!IsResultTypeTrivial && IgnoreNonTrivialTypes))
96 return {
false, FixItHints};
98 if (IsResultTypeTrivial &&
99 static_cast<std::uint64_t
>(
100 Match.Context->getTypeSizeInChars(ResultType).getQuantity()) >
101 IgnoreTrivialTypesOfSizeAbove)
102 return {
false, FixItHints};
104 bool FoundNestedCall =
false;
106 for (
const Expr *Arg : Result.Args) {
107 const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());
112 const QualType ArgType = Arg->IgnoreParenImpCasts()
115 .getUnqualifiedType();
117 if (ArgType == ResultType)
120 const StringRef ArgText = Lexer::getSourceText(
121 CharSourceRange::getTokenRange(Arg->getSourceRange()), SourceMngr,
124 const auto Replacement = Twine(
"static_cast<")
125 .concat(ResultType.getAsString(LanguageOpts))
131 FixItHints.push_back(
132 FixItHint::CreateReplacement(Arg->getSourceRange(), Replacement));
137 if (InnerCall->getDirectCallee()->getQualifiedNameAsString() !=
138 TopCall->getDirectCallee()->getQualifiedNameAsString())
141 const FindArgsResult InnerResult =
findArgs(InnerCall);
144 if (!InnerResult.First || !InnerResult.Last)
148 if ((Result.Compare || InnerResult.Compare) &&
154 FoundNestedCall =
true;
157 FixItHints.push_back(
158 FixItHint::CreateRemoval(InnerCall->getCallee()->getSourceRange()));
162 InnerCall->getCallee()->getEndLoc(), SourceMngr, LanguageOpts);
163 if (LParen.has_value() && LParen->is(tok::l_paren))
164 FixItHints.push_back(
165 FixItHint::CreateRemoval(SourceRange(LParen->getLocation())));
166 FixItHints.push_back(
167 FixItHint::CreateRemoval(SourceRange(InnerCall->getRParenLoc())));
170 if (InnerResult.First == InnerResult.Last) {
172 FixItHints.push_back(FixItHint::CreateRemoval(
173 CharSourceRange::getTokenRange(InnerResult.First->getBeginLoc())));
174 FixItHints.push_back(FixItHint::CreateRemoval(
175 CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
179 Match, InnerCall, InnerResult, IgnoreNonTrivialTypes,
180 IgnoreTrivialTypesOfSizeAbove);
182 FixItHints.append(InnerReplacements);
184 if (InnerResult.Compare) {
187 InnerResult.Last->getEndLoc(), SourceMngr, LanguageOpts);
190 if (Comma.has_value() && Comma->is(tok::comma))
191 FixItHints.push_back(
192 FixItHint::CreateRemoval(SourceRange(Comma->getLocation())));
194 FixItHints.push_back(
195 FixItHint::CreateRemoval(InnerResult.Compare->getSourceRange()));
199 return {FoundNestedCall, FixItHints};
221 auto CreateMatcher = [](
const StringRef FunctionName) {
222 auto FuncDecl = functionDecl(hasName(FunctionName));
223 auto Expression = callExpr(callee(
FuncDecl));
226 anyOf(hasArgument(0, Expression),
227 hasArgument(1, Expression),
228 hasArgument(0, cxxStdInitializerListExpr())),
229 unless(hasParent(Expression)))
233 Finder->addMatcher(CreateMatcher(
"::std::max"),
this);
234 Finder->addMatcher(CreateMatcher(
"::std::min"),
this);
243 const MatchFinder::MatchResult &Match) {
244 const auto *TopCall = Match.Nodes.getNodeAs<CallExpr>(
"topCall");
246 const FindArgsResult Result =
findArgs(TopCall);
247 const auto [FoundNestedCall, Replacements] =
249 IgnoreTrivialTypesOfSizeAbove);
251 if (!FoundNestedCall)
254 const DiagnosticBuilder Diagnostic =
255 diag(TopCall->getBeginLoc(),
256 "do not use nested 'std::%0' calls, use an initializer list instead")
257 << TopCall->getDirectCallee()->getName()
258 << Inserter.createIncludeInsertion(
259 Match.SourceManager->getFileID(TopCall->getBeginLoc()),
263 if (Result.First != Result.Last) {
265 Diagnostic << FixItHint::CreateInsertion(Result.First->getBeginLoc(),
"{");
267 Diagnostic << FixItHint::CreateInsertion(
268 Lexer::getLocForEndOfToken(Result.Last->getEndLoc(), 0,
269 *Match.SourceManager,
270 Match.Context->getLangOpts()),
274 Diagnostic << Replacements;
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.