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,6 +135,11 @@ 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
145
constexpr llvm::StringLiteral Message =
@@ -383,14 +412,40 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
383
412
}
384
413
}
385
414
415
+ UseTrailingReturnTypeCheck::UseTrailingReturnTypeCheck (
416
+ StringRef Name, ClangTidyContext *Context)
417
+ : ClangTidyCheck(Name, Context),
418
+ TransformFunctions (Options.get(" TransformFunctions" , true )),
419
+ TransformLambdas(Options.get(" TransformLambdas" , TransformLambda::All)) {
420
+
421
+ if (TransformFunctions == false && TransformLambdas == TransformLambda::None)
422
+ this ->configurationDiag (
423
+ " The check 'modernize-use-trailing-return-type' will not perform any "
424
+ " analysis because 'TransformFunctions' and 'TransformLambdas' are "
425
+ " disabled." );
426
+ }
427
+
428
+ void UseTrailingReturnTypeCheck::storeOptions (
429
+ ClangTidyOptions::OptionMap &Opts) {
430
+ Options.store (Opts, " TransformFunctions" , TransformFunctions);
431
+ Options.store (Opts, " TransformLambdas" , TransformLambdas);
432
+ }
433
+
386
434
void UseTrailingReturnTypeCheck::registerMatchers (MatchFinder *Finder) {
387
435
auto F = functionDecl (
388
436
unless (anyOf (hasTrailingReturn (), returns (voidType ()),
389
437
cxxConversionDecl (), cxxMethodDecl (isImplicit ()))))
390
438
.bind (" Func" );
391
439
392
- Finder->addMatcher (F, this );
393
- Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
440
+ if (TransformFunctions) {
441
+ Finder->addMatcher (F, this );
442
+ Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
443
+ }
444
+
445
+ if (TransformLambdas != TransformLambda::None) {
446
+ Finder->addMatcher (
447
+ lambdaExpr (unless (hasExplicitResultType ())).bind (" Lambda" ), this );
448
+ }
394
449
}
395
450
396
451
void UseTrailingReturnTypeCheck::registerPPCallbacks (
@@ -402,8 +457,13 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
402
457
assert (PP && " Expected registerPPCallbacks() to have been called before so "
403
458
" preprocessor is available" );
404
459
405
- const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
460
+ if (const auto *Lambda = Result.Nodes .getNodeAs <LambdaExpr>(" Lambda" )) {
461
+ diagOnLambda (Lambda, Result);
462
+ return ;
463
+ }
464
+
406
465
const auto *Fr = Result.Nodes .getNodeAs <FriendDecl>(" Friend" );
466
+ const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
407
467
assert (F && " Matcher is expected to find only FunctionDecls" );
408
468
409
469
// Three-way comparison operator<=> is syntactic sugar and generates implicit
@@ -494,4 +554,75 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
494
554
<< FixItHint::CreateInsertion (InsertionLoc, " -> " + ReturnType);
495
555
}
496
556
557
+ void UseTrailingReturnTypeCheck::diagOnLambda (
558
+ const LambdaExpr *Lambda,
559
+ const ast_matchers::MatchFinder::MatchResult &Result) {
560
+
561
+ const CXXMethodDecl *Method = Lambda->getCallOperator ();
562
+ if (!Method || Lambda->hasExplicitResultType ())
563
+ return ;
564
+
565
+ const QualType ReturnType = Method->getReturnType ();
566
+ if (ReturnType->isUndeducedAutoType () &&
567
+ TransformLambdas == TransformLambda::AllExceptAuto)
568
+ return ;
569
+
570
+ const SourceLocation TrailingReturnInsertLoc =
571
+ findLambdaTrailingReturnInsertLoc (Method, *Result.SourceManager ,
572
+ getLangOpts (), *Result.Context );
573
+
574
+ if (TrailingReturnInsertLoc.isValid ())
575
+ diag (Lambda->getBeginLoc (), " use a trailing return type for this lambda" )
576
+ << FixItHint::CreateInsertion (
577
+ TrailingReturnInsertLoc,
578
+ " -> " +
579
+ ReturnType.getAsString (Result.Context ->getPrintingPolicy ()));
580
+ else
581
+ diag (Lambda->getBeginLoc (), " use a trailing return type for this lambda" );
582
+ }
583
+
584
+ SourceLocation UseTrailingReturnTypeCheck::findLambdaTrailingReturnInsertLoc (
585
+ const CXXMethodDecl *Method, const SourceManager &SM,
586
+ const LangOptions &LangOpts, const ASTContext &Ctx) {
587
+ // 'requires' keyword is present in lambda declaration
588
+ if (Method->getTrailingRequiresClause ()) {
589
+ SourceLocation ParamEndLoc;
590
+ if (Method->param_empty ()) {
591
+ ParamEndLoc = Method->getBeginLoc ();
592
+ } else {
593
+ ParamEndLoc = Method->getParametersSourceRange ().getEnd ();
594
+ }
595
+
596
+ std::pair<FileID, unsigned > ParamEndLocInfo =
597
+ SM.getDecomposedLoc (ParamEndLoc);
598
+ StringRef Buffer = SM.getBufferData (ParamEndLocInfo.first );
599
+
600
+ Lexer Lexer (SM.getLocForStartOfFile (ParamEndLocInfo.first ), LangOpts,
601
+ Buffer.begin (), Buffer.data () + ParamEndLocInfo.second ,
602
+ Buffer.end ());
603
+
604
+ Token Token;
605
+ while (!Lexer.LexFromRawLexer (Token)) {
606
+ if (Token.is (tok::raw_identifier)) {
607
+ IdentifierInfo &Info = Ctx.Idents .get (StringRef (
608
+ SM.getCharacterData (Token.getLocation ()), Token.getLength ()));
609
+ Token.setIdentifierInfo (&Info);
610
+ Token.setKind (Info.getTokenID ());
611
+ }
612
+
613
+ if (Token.is (tok::kw_requires)) {
614
+ return Token.getLocation ().getLocWithOffset (-1 );
615
+ }
616
+ }
617
+
618
+ return {};
619
+ }
620
+
621
+ // If no requires clause, insert before the body
622
+ if (const Stmt *Body = Method->getBody ())
623
+ return Body->getBeginLoc ().getLocWithOffset (-1 );
624
+
625
+ return {};
626
+ }
627
+
497
628
} // namespace clang::tidy::modernize
0 commit comments