1717#include < cctype>
1818#include < optional>
1919
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+
2044using namespace clang ::ast_matchers;
2145
2246namespace clang ::tidy::modernize {
@@ -111,6 +135,11 @@ struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> {
111135private:
112136 const FunctionDecl &F;
113137};
138+
139+ AST_MATCHER (LambdaExpr, hasExplicitResultType) {
140+ return Node.hasExplicitResultType ();
141+ }
142+
114143} // namespace
115144
116145constexpr llvm::StringLiteral Message =
@@ -383,14 +412,40 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
383412 }
384413}
385414
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+
386434void UseTrailingReturnTypeCheck::registerMatchers (MatchFinder *Finder) {
387435 auto F = functionDecl (
388436 unless (anyOf (hasTrailingReturn (), returns (voidType ()),
389437 cxxConversionDecl (), cxxMethodDecl (isImplicit ()))))
390438 .bind (" Func" );
391439
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+ }
394449}
395450
396451void UseTrailingReturnTypeCheck::registerPPCallbacks (
@@ -402,8 +457,13 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
402457 assert (PP && " Expected registerPPCallbacks() to have been called before so "
403458 " preprocessor is available" );
404459
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+
406465 const auto *Fr = Result.Nodes .getNodeAs <FriendDecl>(" Friend" );
466+ const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
407467 assert (F && " Matcher is expected to find only FunctionDecls" );
408468
409469 // Three-way comparison operator<=> is syntactic sugar and generates implicit
@@ -494,4 +554,75 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
494554 << FixItHint::CreateInsertion (InsertionLoc, " -> " + ReturnType);
495555}
496556
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+
497628} // namespace clang::tidy::modernize
0 commit comments