}
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).
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<AutoTypeLoc>();
+ if ((ATL && (ATL.isConstrained() ||
+ ATL.getAutoKeyword() == AutoTypeKeyword::DecltypeAuto)) ||
+ ReturnLoc.getAs<DecltypeTypeLoc>()) {
+ 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<FileID, unsigned> 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;
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) {
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<SmallVector<ClassifiedToken, 8>> 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 =
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);
if (F->getLocation().isInvalid())
return;
+ // Skip functions which return just 'auto'.
+ const auto *AT = F->getDeclaredReturnType()->getAs<AutoType>();
+ 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<DecltypeType>() != nullptr) {
+ F->getDeclaredReturnType()->isMemberPointerType()) {
diag(F->getLocation(), Message);
return;
}
// 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;
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);
-----------------
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.
.. 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.
--- /dev/null
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-trailing-return-type %t
+
+namespace std {
+template <typename T, typename U>
+struct is_same { static constexpr auto value = false; };
+
+template <typename T>
+struct is_same<T, T> { static constexpr auto value = true; };
+
+template <typename T>
+concept floating_point = std::is_same<T, float>::value || std::is_same<T, double>::value || std::is_same<T, long double>::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 <typename T>
+concept Concept = true;
+
+template <typename T, typename U>
+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<int> 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<int> 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 <typename T>
+int req1(T t) requires std::floating_point<T>;
+// 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<T>;{{$}}
+
+template <typename T>
+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; };{{$}}
-// 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 <typename T>
class string;
+ class ostream;
+
template <typename T>
auto declval() -> T;
}
// 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 <typename F, typename T>
decltype(std::declval<F>(std::declval<T>)) 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<F>(std::declval<T>)) { return f(t); }{{$}}
+// CHECK-FIXES: {{^}}auto dec2(F f, T t) -> decltype(std::declval<F>(std::declval<T>)) { return f(t); }{{$}}
template <typename T>
typename decltype(std::declval<T>())::value_type dec3();
// CHECK-MESSAGES: :[[@LINE-1]]:50: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
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
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
template <typename T> auto f(T t) -> int;
auto ff();
-decltype(auto) fff();
void c();
void c(int arg);