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+ " all_except_auto" },
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,10 +135,17 @@ 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 ErrorMessageOnFunction =
117146 " use a trailing return type for this function" ;
147+ constexpr llvm::StringLiteral ErrorMessageOnLambda =
148+ " use a trailing return type for this lambda" ;
118149
119150static SourceLocation expandIfMacroId (SourceLocation Loc,
120151 const SourceManager &SM) {
@@ -329,6 +360,48 @@ findReturnTypeAndCVSourceRange(const FunctionDecl &F, const TypeLoc &ReturnLoc,
329360 return ReturnTypeRange;
330361}
331362
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+
332405static void keepSpecifiers (std::string &ReturnType, std::string &Auto,
333406 SourceRange ReturnTypeCVRange, const FunctionDecl &F,
334407 const FriendDecl *Fr, const ASTContext &Ctx,
@@ -382,14 +455,43 @@ static void keepSpecifiers(std::string &ReturnType, std::string &Auto,
382455 }
383456}
384457
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+
385477void 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+ }
390491
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 );
393495}
394496
395497void UseTrailingReturnTypeCheck::registerPPCallbacks (
@@ -401,8 +503,13 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
401503 assert (PP && " Expected registerPPCallbacks() to have been called before so "
402504 " preprocessor is available" );
403505
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+
405511 const auto *Fr = Result.Nodes .getNodeAs <FriendDecl>(" Friend" );
512+ const auto *F = Result.Nodes .getNodeAs <FunctionDecl>(" Func" );
406513 assert (F && " Matcher is expected to find only FunctionDecls" );
407514
408515 // Three-way comparison operator<=> is syntactic sugar and generates implicit
@@ -495,4 +602,41 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
495602 << FixItHint::CreateInsertion (InsertionLoc, " -> " + ReturnType);
496603}
497604
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+
498642} // namespace clang::tidy::modernize
0 commit comments