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
+ " all_except_auto" },
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
145
constexpr llvm::StringLiteral ErrorMessageOnFunction =
117
146
" use a trailing return type for this function" ;
147
+ constexpr llvm::StringLiteral ErrorMessageOnLambda =
148
+ " use a trailing return type for this lambda" ;
118
149
119
150
static SourceLocation expandIfMacroId (SourceLocation Loc,
120
151
const SourceManager &SM) {
@@ -329,6 +360,48 @@ findReturnTypeAndCVSourceRange(const FunctionDecl &F, const TypeLoc &ReturnLoc,
329
360
return ReturnTypeRange;
330
361
}
331
362
363
+ static SourceLocation findLambdaTrailingReturnInsertLoc (
364
+ const CXXMethodDecl *Method, const SourceManager &SM,
365
+ const LangOptions &LangOpts, const ASTContext &Ctx) {
366
+ // 'requires' keyword is present in lambda declaration
367
+ if (Method->getTrailingRequiresClause ()) {
368
+ SourceLocation ParamEndLoc;
369
+ if (Method->param_empty ())
370
+ ParamEndLoc = Method->getBeginLoc ();
371
+ else
372
+ ParamEndLoc = Method->getParametersSourceRange ().getEnd ();
373
+
374
+ std::pair<FileID, unsigned > ParamEndLocInfo =
375
+ SM.getDecomposedLoc (ParamEndLoc);
376
+ StringRef Buffer = SM.getBufferData (ParamEndLocInfo.first );
377
+
378
+ Lexer Lexer (SM.getLocForStartOfFile (ParamEndLocInfo.first ), LangOpts,
379
+ Buffer.begin (), Buffer.data () + ParamEndLocInfo.second ,
380
+ Buffer.end ());
381
+
382
+ Token Token;
383
+ while (!Lexer.LexFromRawLexer (Token)) {
384
+ if (Token.is (tok::raw_identifier)) {
385
+ IdentifierInfo &Info = Ctx.Idents .get (StringRef (
386
+ SM.getCharacterData (Token.getLocation ()), Token.getLength ()));
387
+ Token.setIdentifierInfo (&Info);
388
+ Token.setKind (Info.getTokenID ());
389
+ }
390
+
391
+ if (Token.is (tok::kw_requires))
392
+ return Token.getLocation ().getLocWithOffset (-1 );
393
+ }
394
+
395
+ return {};
396
+ }
397
+
398
+ // If no requires clause, insert before the body
399
+ if (const Stmt *Body = Method->getBody ())
400
+ return Body->getBeginLoc ().getLocWithOffset (-1 );
401
+
402
+ return {};
403
+ }
404
+
332
405
static void keepSpecifiers (std::string &ReturnType, std::string &Auto,
333
406
SourceRange ReturnTypeCVRange, const FunctionDecl &F,
334
407
const FriendDecl *Fr, const ASTContext &Ctx,
@@ -382,14 +455,43 @@ static void keepSpecifiers(std::string &ReturnType, std::string &Auto,
382
455
}
383
456
}
384
457
458
+ UseTrailingReturnTypeCheck::UseTrailingReturnTypeCheck (
459
+ StringRef Name, ClangTidyContext *Context)
460
+ : ClangTidyCheck(Name, Context),
461
+ TransformFunctions (Options.get(" TransformFunctions" , true )),
462
+ TransformLambdas(Options.get(" TransformLambdas" , TransformLambda::All)) {
463
+
464
+ if (TransformFunctions == false && TransformLambdas == TransformLambda::None)
465
+ this ->configurationDiag (
466
+ " The check 'modernize-use-trailing-return-type' will not perform any "
467
+ " analysis because 'TransformFunctions' and 'TransformLambdas' are "
468
+ " disabled." );
469
+ }
470
+
471
+ void UseTrailingReturnTypeCheck::storeOptions (
472
+ ClangTidyOptions::OptionMap &Opts) {
473
+ Options.store (Opts, " TransformFunctions" , TransformFunctions);
474
+ Options.store (Opts, " TransformLambdas" , TransformLambdas);
475
+ }
476
+
385
477
void UseTrailingReturnTypeCheck::registerMatchers (MatchFinder *Finder) {
386
- auto F = functionDecl (
387
- unless (anyOf (hasTrailingReturn (), returns (voidType ()),
388
- cxxConversionDecl (), cxxMethodDecl (isImplicit ()))))
389
- .bind (" Func" );
478
+ auto F =
479
+ functionDecl (
480
+ unless (anyOf (
481
+ hasTrailingReturn (), returns (voidType ()), cxxConversionDecl (),
482
+ cxxMethodDecl (
483
+ anyOf (isImplicit (),
484
+ hasParent (cxxRecordDecl (hasParent (lambdaExpr ()))))))))
485
+ .bind (" Func" );
486
+
487
+ if (TransformFunctions) {
488
+ Finder->addMatcher (F, this );
489
+ Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
490
+ }
390
491
391
- Finder->addMatcher (F, this );
392
- Finder->addMatcher (friendDecl (hasDescendant (F)).bind (" Friend" ), this );
492
+ if (TransformLambdas != TransformLambda::None)
493
+ Finder->addMatcher (
494
+ lambdaExpr (unless (hasExplicitResultType ())).bind (" Lambda" ), this );
393
495
}
394
496
395
497
void UseTrailingReturnTypeCheck::registerPPCallbacks (
@@ -401,8 +503,13 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
401
503
assert (PP && " Expected registerPPCallbacks() to have been called before so "
402
504
" preprocessor is available" );
403
505
404
- const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
506
+ if (const auto *Lambda = Result.Nodes .getNodeAs <LambdaExpr>(" Lambda" )) {
507
+ diagOnLambda (Lambda, Result);
508
+ return ;
509
+ }
510
+
405
511
const auto *Fr = Result.Nodes .getNodeAs <FriendDecl>(" Friend" );
512
+ const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
406
513
assert (F && " Matcher is expected to find only FunctionDecls" );
407
514
408
515
// Three-way comparison operator<=> is syntactic sugar and generates implicit
@@ -495,4 +602,41 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
495
602
<< FixItHint::CreateInsertion (InsertionLoc, " -> " + ReturnType);
496
603
}
497
604
605
+ void UseTrailingReturnTypeCheck::diagOnLambda (
606
+ const LambdaExpr *Lambda,
607
+ const ast_matchers::MatchFinder::MatchResult &Result) {
608
+
609
+ const CXXMethodDecl *Method = Lambda->getCallOperator ();
610
+ if (!Method || Lambda->hasExplicitResultType ())
611
+ return ;
612
+
613
+ const ASTContext *Ctx = Result.Context ;
614
+ const QualType ReturnType = Method->getReturnType ();
615
+
616
+ // We can't write 'auto' in C++11 mode, try to write generic msg and bail out.
617
+ if (ReturnType->isDependentType () &&
618
+ Ctx->getLangOpts ().LangStd == LangStandard::lang_cxx11) {
619
+ if (TransformLambdas == TransformLambda::All)
620
+ diag (Lambda->getBeginLoc (), ErrorMessageOnLambda);
621
+ return ;
622
+ }
623
+
624
+ if (ReturnType->isUndeducedAutoType () &&
625
+ TransformLambdas == TransformLambda::AllExceptAuto)
626
+ return ;
627
+
628
+ const SourceLocation TrailingReturnInsertLoc =
629
+ findLambdaTrailingReturnInsertLoc (Method, *Result.SourceManager ,
630
+ getLangOpts (), *Result.Context );
631
+
632
+ if (TrailingReturnInsertLoc.isValid ())
633
+ diag (Lambda->getBeginLoc (), " use a trailing return type for this lambda" )
634
+ << FixItHint::CreateInsertion (
635
+ TrailingReturnInsertLoc,
636
+ " -> " +
637
+ ReturnType.getAsString (Result.Context ->getPrintingPolicy ()));
638
+ else
639
+ diag (Lambda->getBeginLoc (), ErrorMessageOnLambda);
640
+ }
641
+
498
642
} // namespace clang::tidy::modernize
0 commit comments