Index: clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h =================================================================== --- clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h +++ clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h @@ -50,10 +50,11 @@ const SourceManager &SM, const LangOptions &LangOpts); SourceRange findReturnTypeAndCVSourceRange(const FunctionDecl &F, + const TypeLoc &ReturnLoc, const ASTContext &Ctx, const SourceManager &SM, const LangOptions &LangOpts); - bool keepSpecifiers(std::string &ReturnType, std::string &Auto, + void keepSpecifiers(std::string &ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange, const FunctionDecl &F, const FriendDecl *Fr, const ASTContext &Ctx, const SourceManager &SM, const LangOptions &LangOpts); Index: clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp +++ clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp @@ -261,8 +261,8 @@ } SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange( - const FunctionDecl &F, const ASTContext &Ctx, const SourceManager &SM, - const LangOptions &LangOpts) { + const FunctionDecl &F, const TypeLoc &ReturnLoc, const ASTContext &Ctx, + const SourceManager &SM, const LangOptions &LangOpts) { // We start with the range of the return type and expand to neighboring // qualifiers (const, volatile and restrict). @@ -274,6 +274,35 @@ return {}; } + // If the return type is a constrained 'auto' or 'decltype(auto)', we need to + // include the tokens after the concept. Unfortunately, the source range of an + // AutoTypeLoc, if it is constrained, does not include the 'auto' or + // 'decltype(auto)'. If the return type is a plain 'decltype(...)', the + // source range only contains the first 'decltype' token. + auto ATL = ReturnLoc.getAs(); + if ((ATL && (ATL.isConstrained() || + ATL.getAutoKeyword() == AutoTypeKeyword::DecltypeAuto)) || + ReturnLoc.getAs()) { + SourceLocation End = + expandIfMacroId(ReturnLoc.getSourceRange().getEnd(), SM); + SourceLocation BeginNameF = expandIfMacroId(F.getLocation(), SM); + + // Extend the ReturnTypeRange until the last token before the function + // name. + std::pair Loc = SM.getDecomposedLoc(End); + StringRef File = SM.getBufferData(Loc.first); + const char *TokenBegin = File.data() + Loc.second; + Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(), + TokenBegin, File.end()); + Token T; + SourceLocation LastTLoc = End; + while (!Lexer.LexFromRawLexer(T) && + SM.isBeforeInTranslationUnit(T.getLocation(), BeginNameF)) { + LastTLoc = T.getLocation(); + } + ReturnTypeRange.setEnd(LastTLoc); + } + // If the return type has no local qualifiers, it's source range is accurate. if (!hasAnyNestedLocalQualifiers(F.getReturnType())) return ReturnTypeRange; @@ -317,7 +346,7 @@ return ReturnTypeRange; } -bool UseTrailingReturnTypeCheck::keepSpecifiers( +void UseTrailingReturnTypeCheck::keepSpecifiers( std::string &ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange, const FunctionDecl &F, const FriendDecl *Fr, const ASTContext &Ctx, const SourceManager &SM, const LangOptions &LangOpts) { @@ -327,14 +356,14 @@ if (!F.isConstexpr() && !F.isInlineSpecified() && F.getStorageClass() != SC_Extern && F.getStorageClass() != SC_Static && !Fr && !(M && M->isVirtualAsWritten())) - return true; + return; // Tokenize return type. If it contains macros which contain a mix of // qualifiers, specifiers and types, give up. llvm::Optional> MaybeTokens = classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts); if (!MaybeTokens) - return false; + return; // Find specifiers, remove them from the return type, add them to 'auto'. unsigned int ReturnTypeBeginOffset = @@ -367,14 +396,12 @@ ReturnType.erase(TOffsetInRT, TLengthWithWS); DeletedChars += TLengthWithWS; } - - return true; } void UseTrailingReturnTypeCheck::registerMatchers(MatchFinder *Finder) { - auto F = functionDecl(unless(anyOf(hasTrailingReturn(), returns(voidType()), - returns(autoType()), cxxConversionDecl(), - cxxMethodDecl(isImplicit())))) + auto F = functionDecl( + unless(anyOf(hasTrailingReturn(), returns(voidType()), + cxxConversionDecl(), cxxMethodDecl(isImplicit())))) .bind("Func"); Finder->addMatcher(F, this); @@ -397,11 +424,17 @@ if (F->getLocation().isInvalid()) return; + // Skip functions which return just 'auto'. + const auto *AT = F->getDeclaredReturnType()->getAs(); + if (AT != nullptr && !AT->isConstrained() && + AT->getKeyword() == AutoTypeKeyword::Auto && + !hasAnyNestedLocalQualifiers(F->getDeclaredReturnType())) + return; + // TODO: implement those if (F->getDeclaredReturnType()->isFunctionPointerType() || F->getDeclaredReturnType()->isMemberFunctionPointerType() || - F->getDeclaredReturnType()->isMemberPointerType() || - F->getDeclaredReturnType()->getAs() != nullptr) { + F->getDeclaredReturnType()->isMemberPointerType()) { diag(F->getLocation(), Message); return; } @@ -435,7 +468,7 @@ // discards user formatting and order of const, volatile, type, whitespace, // space before & ... . SourceRange ReturnTypeCVRange = - findReturnTypeAndCVSourceRange(*F, Ctx, SM, LangOpts); + findReturnTypeAndCVSourceRange(*F, FTL.getReturnLoc(), Ctx, SM, LangOpts); if (ReturnTypeCVRange.isInvalid()) return; Index: clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst +++ clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst @@ -29,10 +29,10 @@ ----------------- The following categories of return types cannot be rewritten currently: + * function pointers * member function pointers * member pointers -* decltype, when it is the top level expression Unqualified names in the return type might erroneously refer to different entities after the rewrite. Preventing such errors requires a full lookup of all unqualified names present in the return type in the scope of the trailing return type location. @@ -43,26 +43,26 @@ .. code-block:: c++ - struct Object { long long value; }; - Object f(unsigned Object) { return {Object * 2}; } + struct S { long long value; }; + S f(unsigned S) { return {S * 2}; } class CC { - int Object; - struct Object m(); + int S; + struct S m(); }; - Object CC::m() { return {0}; } + S CC::m() { return {0}; } a careless rewrite would produce the following output: .. code-block:: c++ - struct Object { long long value; }; - auto f(unsigned Object) -> Object { return {Object * 2}; } // error + struct S { long long value; }; + auto f(unsigned S) -> S { return {S * 2}; } // error class CC { - int Object; - auto m() -> struct Object; + int S; + auto m() -> struct S; }; - auto CC::m() -> Object { return {0}; } // error + auto CC::m() -> S { return {0}; } // error -This code fails to compile because the Object in the context of f refers to the equally named function parameter. -Similarly, the Object in the context of m refers to the equally named class member. -The check can currently only detect a clash with a function parameter name. +This code fails to compile because the S in the context of f refers to the equally named function parameter. +Similarly, the S in the context of m refers to the equally named class member. +The check can currently only detect and avoid a clash with a function parameter name. Index: clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp @@ -0,0 +1,54 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-trailing-return-type %t + +namespace std { +template +struct is_same { static constexpr auto value = false; }; + +template +struct is_same { static constexpr auto value = true; }; + +template +concept floating_point = std::is_same::value || std::is_same::value || std::is_same::value; +} + +// +// Concepts +// + +std::floating_point auto con1(); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto con1() -> std::floating_point auto;{{$}} + +std::floating_point auto con1() { return 3.14f; } +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto con1() -> std::floating_point auto { return 3.14f; }{{$}} + +namespace a { +template +concept Concept = true; + +template +concept BinaryConcept = true; +} + +a::Concept decltype(auto) con2(); +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto con2() -> a::Concept decltype(auto);{{$}} + +a::BinaryConcept decltype(auto) con3(); +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto con3() -> a::BinaryConcept decltype(auto);{{$}} + +const std::floating_point auto* volatile con4(); +// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto con4() -> const std::floating_point auto* volatile;{{$}} + +template +int req1(T t) requires std::floating_point; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto req1(T t) -> int requires std::floating_point;{{$}} + +template +T req2(T t) requires requires { t + t; }; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a trailing return type for this function [modernize-use-trailing-return-type] + // CHECK-FIXES: {{^}}auto req2(T t) -> T requires requires { t + t; };{{$}} Index: clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp +++ clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp @@ -1,6 +1,4 @@ -// RUN: %check_clang_tidy -std=c++14,c++17 %s modernize-use-trailing-return-type %t -- -- -fdeclspec -fexceptions -// FIXME: Fix the checker to work in C++20 mode, it is performing a -// use-of-uninitialized-value. +// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-use-trailing-return-type %t -- -- -fdeclspec -fexceptions -DCOMMAND_LINE_INT=int namespace std { template @@ -11,6 +9,8 @@ class string; + class ostream; + template auto declval() -> T; } @@ -215,18 +215,28 @@ // CHECK-FIXES: {{^}}auto e13() -> struct A;{{$}} // -// decltype (unsupported if top level expression) +// deduced return types // +const auto ded1(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto ded1() -> const auto;{{$}} +const auto& ded2(); +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto ded2() -> const auto&;{{$}} + +decltype(auto) ded3(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto ded3() -> decltype(auto);{{$}} + + decltype(1 + 2) dec1() { return 1 + 2; } // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use a trailing return type for this function [modernize-use-trailing-return-type] -// TODO: source range of DecltypeTypeLoc not yet implemented -// _HECK-FIXES: {{^}}auto dec1() -> decltype(1 + 2) { return 1 + 2; }{{$}} +// CHECK-FIXES: {{^}}auto dec1() -> decltype(1 + 2) { return 1 + 2; }{{$}} template decltype(std::declval(std::declval)) dec2(F f, T t) { return f(t); } // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: use a trailing return type for this function [modernize-use-trailing-return-type] -// TODO: source range of DecltypeTypeLoc not yet implemented -// _HECK-FIXES: {{^}}auto dec2(F f, T t) -> decltype(std::declval(std::declval)) { return f(t); }{{$}} +// CHECK-FIXES: {{^}}auto dec2(F f, T t) -> decltype(std::declval(std::declval)) { return f(t); }{{$}} template typename decltype(std::declval())::value_type dec3(); // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: use a trailing return type for this function [modernize-use-trailing-return-type] @@ -463,6 +473,13 @@ CONST_F_MACRO int& h19(); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type] // CHECK-FIXES: {{^}}auto h19() -> CONST_F_MACRO int&;{{$}} +// Macro COMMAND_LINE_INT is defined on the command line via: -DCOMMAND_LINE_INT=int +const COMMAND_LINE_INT& h20(); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h20() -> const COMMAND_LINE_INT&;{{$}} +decltype(COMMAND_LINE_INT{}) h21(); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h21() -> decltype(COMMAND_LINE_INT{});{{$}} // // Name collisions @@ -531,6 +548,14 @@ return {0}; } +// +// bug 44206, no rewrite should happen due to collision with parameter name +// + +using std::ostream; +ostream& operator<<(ostream& ostream, int i); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}ostream& operator<<(ostream& ostream, int i);{{$}} // // Samples which do not trigger the check @@ -544,7 +569,6 @@ template auto f(T t) -> int; auto ff(); -decltype(auto) fff(); void c(); void c(int arg);