[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