17
17
#include < cctype>
18
18
#include < optional>
19
19
20
+ namespace clang ::tidy {
21
+
22
+ template <>
23
+ struct OptionEnumMapping <
24
+ modernize::UseTrailingReturnTypeCheck::TransformLambda> {
25
+ static llvm::ArrayRef<std::pair<
26
+ modernize::UseTrailingReturnTypeCheck::TransformLambda, StringRef>>
27
+ getEnumMapping () {
28
+ static constexpr std::pair<
29
+ modernize::UseTrailingReturnTypeCheck::TransformLambda, StringRef>
30
+ Mapping[] = {
31
+ {modernize::UseTrailingReturnTypeCheck::TransformLambda::All,
32
+ " All" },
33
+ {modernize::UseTrailingReturnTypeCheck::TransformLambda::
34
+ AllExceptAuto,
35
+ " AllExceptAuto" },
36
+ {modernize::UseTrailingReturnTypeCheck::TransformLambda::None,
37
+ " None" }};
38
+ return Mapping;
39
+ }
40
+ };
41
+
42
+ } // namespace clang::tidy
43
+
20
44
using namespace clang ::ast_matchers;
21
45
22
46
namespace clang ::tidy::modernize {
@@ -111,10 +135,17 @@ struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> {
111
135
private:
112
136
const FunctionDecl &F;
113
137
};
138
+
139
+ AST_MATCHER (LambdaExpr, hasExplicitResultType) {
140
+ return Node.hasExplicitResultType ();
141
+ }
142
+
114
143
} // namespace
115
144
116
- constexpr llvm::StringLiteral Message =
145
+ constexpr llvm::StringLiteral MessageFunction =
117
146
" use a trailing return type for this function" ;
147
+ constexpr llvm::StringLiteral MessageLambda =
148
+ " use a trailing return type for this lambda" ;
118
149
119
150
static SourceLocation expandIfMacroId (SourceLocation Loc,
120
151
const SourceManager &SM) {
@@ -242,7 +273,7 @@ UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
242
273
const MacroInfo *MI = PP->getMacroInfo (&Info);
243
274
if (!MI || MI->isFunctionLike ()) {
244
275
// Cannot handle function style macros.
245
- diag (F.getLocation (), Message );
276
+ diag (F.getLocation (), MessageFunction );
246
277
return std::nullopt;
247
278
}
248
279
}
@@ -254,7 +285,7 @@ UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
254
285
if (std::optional<ClassifiedToken> CT = classifyToken (F, *PP, T))
255
286
ClassifiedTokens.push_back (*CT);
256
287
else {
257
- diag (F.getLocation (), Message );
288
+ diag (F.getLocation (), MessageFunction );
258
289
return std::nullopt;
259
290
}
260
291
}
@@ -283,7 +314,7 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
283
314
if (ReturnTypeRange.isInvalid ()) {
284
315
// Happens if e.g. clang cannot resolve all includes and the return type is
285
316
// unknown.
286
- diag (F.getLocation (), Message );
317
+ diag (F.getLocation (), MessageFunction );
287
318
return {};
288
319
}
289
320
@@ -383,14 +414,44 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
383
414
}
384
415
}
385
416
417
+ UseTrailingReturnTypeCheck::UseTrailingReturnTypeCheck (
418
+ StringRef Name, ClangTidyContext *Context)
419
+ : ClangTidyCheck(Name, Context),
420
+ TransformFunctions (Options.get(" TransformFunctions" , true )),
421
+ TransformLambdas(Options.get(" TransformLambdas" , TransformLambda::All)) {
422
+
423
+ if (TransformFunctions == false && TransformLambdas == TransformLambda::None)
424
+ this ->configurationDiag (
425
+ " The check 'modernize-use-trailing-return-type' will not perform any "
426
+ " analysis because 'TransformFunctions' and 'TransformLambdas' are "
427
+ " disabled." );
428
+ }
429
+
430
+ void UseTrailingReturnTypeCheck::storeOptions (
431
+ ClangTidyOptions::OptionMap &Opts) {
432
+ Options.store (Opts, " TransformFunctions" , TransformFunctions);
433
+ Options.store (Opts, " TransformLambdas" , TransformLambdas);
434
+ }
435
+
386
436
void UseTrailingReturnTypeCheck::registerMatchers (MatchFinder *Finder) {
387
- auto F = functionDecl (
388
- unless (anyOf (hasTrailingReturn (), returns (voidType ()),
389
- cxxConversionDecl (), cxxMethodDecl (isImplicit ()))))
390
- .bind (" Func" );
437
+ auto F =
438
+ functionDecl (
439
+ unless (anyOf (
440
+ hasTrailingReturn (), returns (voidType ()), cxxConversionDecl (),
441
+ cxxMethodDecl (
442
+ anyOf (isImplicit (),
443
+ hasParent (cxxRecordDecl (hasParent (lambdaExpr ()))))))))
444
+ .bind (" Func" );
445
+
446
+ if (TransformFunctions) {
447
+ Finder->addMatcher (F, this );
448
+ Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
449
+ }
391
450
392
- Finder->addMatcher (F, this );
393
- Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
451
+ if (TransformLambdas != TransformLambda::None) {
452
+ Finder->addMatcher (
453
+ lambdaExpr (unless (hasExplicitResultType ())).bind (" Lambda" ), this );
454
+ }
394
455
}
395
456
396
457
void UseTrailingReturnTypeCheck::registerPPCallbacks (
@@ -402,8 +463,13 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
402
463
assert (PP && " Expected registerPPCallbacks() to have been called before so "
403
464
" preprocessor is available" );
404
465
405
- const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
466
+ if (const auto *Lambda = Result.Nodes .getNodeAs <LambdaExpr>(" Lambda" )) {
467
+ diagOnLambda (Lambda, Result);
468
+ return ;
469
+ }
470
+
406
471
const auto *Fr = Result.Nodes .getNodeAs <FriendDecl>(" Friend" );
472
+ const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
407
473
assert (F && " Matcher is expected to find only FunctionDecls" );
408
474
409
475
// Three-way comparison operator<=> is syntactic sugar and generates implicit
@@ -423,7 +489,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
423
489
if (F->getDeclaredReturnType ()->isFunctionPointerType () ||
424
490
F->getDeclaredReturnType ()->isMemberFunctionPointerType () ||
425
491
F->getDeclaredReturnType ()->isMemberPointerType ()) {
426
- diag (F->getLocation (), Message );
492
+ diag (F->getLocation (), MessageFunction );
427
493
return ;
428
494
}
429
495
@@ -440,14 +506,14 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
440
506
// FIXME: This may happen if we have __attribute__((...)) on the function.
441
507
// We abort for now. Remove this when the function type location gets
442
508
// available in clang.
443
- diag (F->getLocation (), Message );
509
+ diag (F->getLocation (), MessageFunction );
444
510
return ;
445
511
}
446
512
447
513
SourceLocation InsertionLoc =
448
514
findTrailingReturnTypeSourceLocation (*F, FTL, Ctx, SM, LangOpts);
449
515
if (InsertionLoc.isInvalid ()) {
450
- diag (F->getLocation (), Message );
516
+ diag (F->getLocation (), MessageFunction );
451
517
return ;
452
518
}
453
519
@@ -470,7 +536,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
470
536
UnqualNameVisitor UNV{*F};
471
537
UNV.TraverseTypeLoc (FTL.getReturnLoc ());
472
538
if (UNV.Collision ) {
473
- diag (F->getLocation (), Message );
539
+ diag (F->getLocation (), MessageFunction );
474
540
return ;
475
541
}
476
542
@@ -489,9 +555,90 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
489
555
keepSpecifiers (ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM,
490
556
LangOpts);
491
557
492
- diag (F->getLocation (), Message )
558
+ diag (F->getLocation (), MessageFunction )
493
559
<< FixItHint::CreateReplacement (ReturnTypeCVRange, Auto)
494
560
<< FixItHint::CreateInsertion (InsertionLoc, " -> " + ReturnType);
495
561
}
496
562
563
+ void UseTrailingReturnTypeCheck::diagOnLambda (
564
+ const LambdaExpr *Lambda,
565
+ const ast_matchers::MatchFinder::MatchResult &Result) {
566
+
567
+ const CXXMethodDecl *Method = Lambda->getCallOperator ();
568
+ if (!Method || Lambda->hasExplicitResultType ())
569
+ return ;
570
+
571
+ const ASTContext *Ctx = Result.Context ;
572
+ const QualType ReturnType = Method->getReturnType ();
573
+
574
+ // We can't write 'auto' in C++11 mode, try to write generic msg and bail out.
575
+ if (ReturnType->isDependentType () &&
576
+ Ctx->getLangOpts ().LangStd == LangStandard::lang_cxx11) {
577
+ if (TransformLambdas == TransformLambda::All)
578
+ diag (Lambda->getBeginLoc (), MessageLambda);
579
+ return ;
580
+ }
581
+
582
+ if (ReturnType->isUndeducedAutoType () &&
583
+ TransformLambdas == TransformLambda::AllExceptAuto)
584
+ return ;
585
+
586
+ const SourceLocation TrailingReturnInsertLoc =
587
+ findLambdaTrailingReturnInsertLoc (Method, *Result.SourceManager ,
588
+ getLangOpts (), *Result.Context );
589
+
590
+ if (TrailingReturnInsertLoc.isValid ())
591
+ diag (Lambda->getBeginLoc (), " use a trailing return type for this lambda" )
592
+ << FixItHint::CreateInsertion (
593
+ TrailingReturnInsertLoc,
594
+ " -> " +
595
+ ReturnType.getAsString (Result.Context ->getPrintingPolicy ()));
596
+ else
597
+ diag (Lambda->getBeginLoc (), MessageLambda);
598
+ }
599
+
600
+ SourceLocation UseTrailingReturnTypeCheck::findLambdaTrailingReturnInsertLoc (
601
+ const CXXMethodDecl *Method, const SourceManager &SM,
602
+ const LangOptions &LangOpts, const ASTContext &Ctx) {
603
+ // 'requires' keyword is present in lambda declaration
604
+ if (Method->getTrailingRequiresClause ()) {
605
+ SourceLocation ParamEndLoc;
606
+ if (Method->param_empty ()) {
607
+ ParamEndLoc = Method->getBeginLoc ();
608
+ } else {
609
+ ParamEndLoc = Method->getParametersSourceRange ().getEnd ();
610
+ }
611
+
612
+ std::pair<FileID, unsigned > ParamEndLocInfo =
613
+ SM.getDecomposedLoc (ParamEndLoc);
614
+ StringRef Buffer = SM.getBufferData (ParamEndLocInfo.first );
615
+
616
+ Lexer Lexer (SM.getLocForStartOfFile (ParamEndLocInfo.first ), LangOpts,
617
+ Buffer.begin (), Buffer.data () + ParamEndLocInfo.second ,
618
+ Buffer.end ());
619
+
620
+ Token Token;
621
+ while (!Lexer.LexFromRawLexer (Token)) {
622
+ if (Token.is (tok::raw_identifier)) {
623
+ IdentifierInfo &Info = Ctx.Idents .get (StringRef (
624
+ SM.getCharacterData (Token.getLocation ()), Token.getLength ()));
625
+ Token.setIdentifierInfo (&Info);
626
+ Token.setKind (Info.getTokenID ());
627
+ }
628
+
629
+ if (Token.is (tok::kw_requires)) {
630
+ return Token.getLocation ().getLocWithOffset (-1 );
631
+ }
632
+ }
633
+
634
+ return {};
635
+ }
636
+
637
+ // If no requires clause, insert before the body
638
+ if (const Stmt *Body = Method->getBody ())
639
+ return Body->getBeginLoc ().getLocWithOffset (-1 );
640
+
641
+ return {};
642
+ }
643
+
497
644
} // namespace clang::tidy::modernize
0 commit comments