[clang-tools-extra] 3450533 - Add support for C++20 concepts and decltype to modernize-use-trailing-return-type.
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 15 07:42:05 PDT 2020
Author: Bernhard Manfred Gruber
Date: 2020-08-15T10:40:22-04:00
New Revision: 345053390ac17dd4a2e759de9e0e24c2605035db
URL: https://github.com/llvm/llvm-project/commit/345053390ac17dd4a2e759de9e0e24c2605035db
DIFF: https://github.com/llvm/llvm-project/commit/345053390ac17dd4a2e759de9e0e24c2605035db.diff
LOG: Add support for C++20 concepts and decltype to modernize-use-trailing-return-type.
Added:
clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp
Modified:
clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h
clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst
clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
index 8e31b4b5f75f..b66e24d58b2f 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
@@ -261,8 +261,8 @@ static bool hasAnyNestedLocalQualifiers(QualType Type) {
}
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 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
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;
@@ -317,7 +346,7 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
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 @@ bool UseTrailingReturnTypeCheck::keepSpecifiers(
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 =
@@ -367,14 +396,12 @@ bool UseTrailingReturnTypeCheck::keepSpecifiers(
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 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
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;
}
@@ -435,7 +468,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
// 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;
diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h
index 0cd4e7741c3e..72643ee02ef4 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.h
@@ -50,10 +50,11 @@ class UseTrailingReturnTypeCheck : public ClangTidyCheck {
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);
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst
index 241071c4489d..0593a35326aa 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-trailing-return-type.rst
@@ -29,10 +29,10 @@ Known Limitations
-----------------
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
diff erent 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 @@ Given the following piece of code
.. 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.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp
new file mode 100644
index 000000000000..de2198437e10
--- /dev/null
+++ b/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 <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; };{{$}}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp
index bf347f772ab4..d5087b598f29 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type.cpp
+++ b/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 <typename T>
@@ -11,6 +9,8 @@ namespace std {
class string;
+ class ostream;
+
template <typename T>
auto declval() -> T;
}
@@ -215,18 +215,28 @@ struct A e13();
// 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]
@@ -463,6 +473,13 @@ CONST_CAT int& h19();
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 @@ Object DD::g() {
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 @@ auto f(int arg1, int arg2, int arg3, ...) -> int;
template <typename T> auto f(T t) -> int;
auto ff();
-decltype(auto) fff();
void c();
void c(int arg);
More information about the cfe-commits
mailing list