bernhardmgruber created this revision.
bernhardmgruber added a reviewer: alexfh.
bernhardmgruber added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, mgorny.

The new clang-tidy pass modernize-use-trailing-return rewrites function 
signatures to use a trailing return type.

A fair amount of tests are included.

Does not work on return types which span locations before and after the 
function name (e.g. functions returning function pointers). The pass may fail 
if the return types are from missing headers (e.g. when clang-tidy is run 
without a compilation database or all needed include directories)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D56160

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/UseTrailingReturnCheck.cpp
  clang-tidy/modernize/UseTrailingReturnCheck.h
  docs/clang-tidy/checks/modernize-use-trailing-return.rst
  test/clang-tidy/modernize-use-trailing-return.cpp

Index: test/clang-tidy/modernize-use-trailing-return.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-trailing-return.cpp
@@ -0,0 +1,144 @@
+// RUN: %check_clang_tidy %s modernize-use-trailing-return %t -- -- --std=c++14
+
+namespace std {
+    template <typename T>
+    class vector;
+
+    class string;
+}
+
+//
+// Samples triggering the check
+//
+
+int f();
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto f() -> int;{{$}}
+int f(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto f(int) -> int;{{$}}
+int f(int arg);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto f(int arg) -> int;{{$}}
+int f(int arg1, int arg2, int arg3);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto f(int arg1, int arg2, int arg3) -> int;{{$}}
+int f(int arg1, int arg2, int arg3, ...);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto f(int arg1, int arg2, int arg3, ...) -> int;{{$}}
+template <typename T> int f(T t);
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}template <typename T> auto f(T t) -> int;{{$}}
+int a1() { return 42; }
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto a1() -> int { return 42; }{{$}}
+int a2() {
+    return 42;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto a2() -> int {{{$}}
+int a3()
+{
+    return 42;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto a3() -> int{{$}}
+int b(int   arg   )   ;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto b(int   arg   ) -> int   ;{{$}}
+inline int d1(int arg);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto d1(int arg) -> int;{{$}}
+inline int d2(int arg) noexcept(true);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto d2(int arg) -> int noexcept(true);{{$}}
+inline int d3(int arg) try { } catch(...) { }
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto d3(int arg) -> int try { } catch(...) { }{{$}}
+namespace N {
+    bool e1();
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:10: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    auto e1() -> bool;{{$}}
+inline volatile const std::vector<std::string> e2();
+// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto e2() -> volatile const std::vector<std::string>;{{$}}
+inline const std::vector<std::string> volatile e2();
+// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto e2() -> const std::vector<std::string> volatile;{{$}}
+inline std::vector<std::string> const volatile e2();
+// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}inline auto e2() -> std::vector<std::string> const volatile;{{$}}
+bool N::e1() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}auto N::e1() -> bool {}{{$}}
+int (*e3())(double);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a trailing return type for this function (FixIt not implemented) [modernize-use-trailing-return]
+// TODO: not matched by the AST matcher
+//decltype(auto) e4();
+// _HECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// _HECK-FIXES: {{^}}auto e4() -> decltype(auto);{{$}}
+
+struct B {
+    double base1(int, bool b);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    auto base1(int, bool b) -> double;{{$}}
+
+    virtual double base2(int, bool b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    virtual auto base2(int, bool b) -> double {}{{$}}
+
+    virtual float base3() const = 0;
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    virtual auto base3() const -> float = 0;{{$}}
+
+    double base4(int, bool b) &&;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    auto base4(int, bool b) && -> double;{{$}}
+
+    double base5(int, bool b) const &&;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    auto base5(int, bool b) const && -> double;{{$}}
+
+    double base6(int, bool b) const & = delete;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    auto base6(int, bool b) const & -> double = delete;{{$}}
+};
+
+struct D : B {
+    virtual double f1(int, bool b) final;
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    virtual auto f1(int, bool b) -> double final;{{$}}
+
+    virtual double base2(int, bool b) override;
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    virtual auto base2(int, bool b) -> double override;{{$}}
+
+    virtual float base3() const override final { }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return]
+// CHECK-FIXES: {{^}}    virtual auto base3() const -> float override final { }{{$}}
+};
+
+//
+// Samples which do not trigger the check
+//
+
+auto ff();
+auto f() -> int;
+auto f(int) -> int;
+auto f(int arg) -> int;
+auto f(int arg1, int arg2, int arg3) -> int;
+auto f(int arg1, int arg2, int arg3, ...) -> int;
+template <typename T> auto f(T t) -> int;
+
+void c();
+void c(int arg);
+void c(int arg) { return; }
+
+struct D2 : B {
+    virtual auto f1(int, bool b) -> double final;
+    virtual auto base2(int, bool b) -> double override;
+    virtual auto base3() const -> float override final { }
+
+    operator double();
+};
Index: docs/clang-tidy/checks/modernize-use-trailing-return.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/modernize-use-trailing-return.rst
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - modernize-use-trailing-return
+
+modernize-use-trailing-return
+======================
+
+
+Rewrites function signatures to use a trailing return type.
Index: clang-tidy/modernize/UseTrailingReturnCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseTrailingReturnCheck.h
@@ -0,0 +1,35 @@
+//===--- UseTrailingReturnCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USETRAILINGRETURNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USETRAILINGRETURNCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Rewrites function signatures to use a trailing return type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-trailing-return.html
+class UseTrailingReturnCheck : public ClangTidyCheck {
+public:
+  UseTrailingReturnCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USETRAILINGRETURNCHECK_H
Index: clang-tidy/modernize/UseTrailingReturnCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseTrailingReturnCheck.cpp
@@ -0,0 +1,209 @@
+//===--- UseTrailingReturnCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseTrailingReturnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+// very similar to UseOverrideCheck
+SourceLocation findTrailingReturnTypeLocation(const CharSourceRange &range,
+                                              const ASTContext &ctx,
+                                              const SourceManager &sm,
+                                              const LangOptions &langOpts) {
+  const std::pair<FileID, unsigned> loc = sm.getDecomposedLoc(range.getBegin());
+  const 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;
+  int nestedParens = 0;
+  SourceLocation result;
+  while (!lexer.LexFromRawLexer(t)) {
+    if (sm.isBeforeInTranslationUnit(range.getEnd(), t.getLocation()))
+      break;
+    if (result.isValid()) {
+      // we found the closing parenthesis, skip additional const and ref
+      // qualifiers
+
+      if (t.is(tok::amp) || t.is(tok::ampamp)) {
+        result = t.getEndLoc();
+        continue;
+      }
+
+      if (t.is(tok::raw_identifier)) {
+        IdentifierInfo &Info = ctx.Idents.get(
+            StringRef(sm.getCharacterData(t.getLocation()), t.getLength()));
+        t.setIdentifierInfo(&Info);
+        t.setKind(Info.getTokenID());
+      }
+
+      if (t.is(tok::kw_const)) {
+        result = t.getEndLoc();
+        continue;
+      }
+
+      return result;
+    }
+    if (t.is(tok::l_paren))
+      ++nestedParens;
+    else if (t.is(tok::r_paren)) {
+      --nestedParens;
+      if (nestedParens == 0)
+        result = t.getEndLoc(); // store location, next token might be const
+    }
+  }
+
+  if (result.isValid())
+    return result;
+
+  llvm_unreachable("Expected to find a closing paranthesis");
+}
+
+SourceRange findReturnTypeAndCVRange(const FunctionDecl &f,
+                                     const ASTContext &ctx,
+                                     const SourceManager &sm,
+                                     const LangOptions &langOpts) {
+
+  // we start with the range of the return type and expand to neighboring const
+  // and volatile
+  auto returnTypeRange = f.getReturnTypeSourceRange();
+
+  // create tokens for everything before the name of the function
+  const std::pair<FileID, unsigned> loc = sm.getDecomposedLoc(f.getBeginLoc());
+  const 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;
+  std::vector<Token> tokens;
+  while (!lexer.LexFromRawLexer(t)) {
+    if (sm.isBeforeInTranslationUnit(f.getLocation(), t.getLocation()))
+      break;
+    if (t.is(tok::raw_identifier)) {
+      IdentifierInfo &Info = ctx.Idents.get(
+          StringRef(sm.getCharacterData(t.getLocation()), t.getLength()));
+      t.setIdentifierInfo(&Info);
+      t.setKind(Info.getTokenID());
+    }
+    tokens.push_back(t);
+  }
+
+  // include const and volatile to the left and right of the return type
+  auto isCV = [](Token t) {
+    return t.is(tok::kw_const) || t.is(tok::kw_volatile);
+  };
+
+  bool extendedLeft = false;
+  for (int i = 0; i < tokens.size(); i++) {
+    // if we found the beginning of the return type, include const and volatile
+    // to the left
+    if (!sm.isBeforeInTranslationUnit(tokens[i].getLocation(),
+                                      returnTypeRange.getBegin()) &&
+        !extendedLeft) {
+      for (int j = i - 1; j >= 0 && isCV(tokens[j]); j--)
+        returnTypeRange.setBegin(tokens[j].getLocation());
+      extendedLeft = true;
+    }
+    // if we found the end of the return type, include const and volatile to the
+    // right
+    if (sm.isBeforeInTranslationUnit(returnTypeRange.getEnd(),
+                                     tokens[i].getLocation())) {
+      for (int j = i; j < tokens.size() && isCV(tokens[j]); j++)
+        returnTypeRange.setEnd(tokens[j].getLocation());
+      break;
+    }
+  }
+
+  return returnTypeRange;
+}
+} // namespace
+
+void UseTrailingReturnCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      functionDecl(unless(anyOf(hasTrailingReturn(), returns(voidType()),
+                                returns(autoType()), cxxConversionDecl())))
+          .bind("f"),
+      this);
+}
+
+void UseTrailingReturnCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *f = Result.Nodes.getNodeAs<FunctionDecl>("f");
+  if (!f)
+    return;
+
+  if (const auto *m = dyn_cast<CXXMethodDecl>(f))
+    if (m->isImplicit() || m->getLocation().isMacroID() || m->isOutOfLine())
+      return;
+
+  if (f->getDeclaredReturnType()->isFunctionPointerType()) {
+    auto d = diag(
+        f->getLocation(),
+        "use a trailing return type for this function (FixIt not implemented)");
+    return;
+  }
+
+  const auto &ctx = *Result.Context;
+  const auto &sm = *Result.SourceManager;
+  const auto &opts = getLangOpts();
+
+  auto returnTypeCVRange = findReturnTypeAndCVRange(*f, ctx, sm, opts);
+  if (!returnTypeCVRange.isValid()) {
+    diag(f->getLocation(),
+         "use a trailing return type for this function (failed to determine "
+         "return type source range, could clang resolve all #includes?)",
+         DiagnosticIDs::Level::Error);
+    return;
+  }
+
+  const CharSourceRange charRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(f->getSourceRange()), sm, opts);
+  if (!charRange.isValid())
+    return;
+  const auto insertionLoc =
+      findTrailingReturnTypeLocation(charRange, ctx, sm, opts);
+
+  // extract return type as string
+  const auto returnType = [&] {
+    // using the declared return type discards user formatting and order (order
+    // of const, volatile, type, whitespace, space before & ...) return
+    // f->getDeclaredReturnType().getAsString();
+
+    const std::pair<FileID, unsigned> beg =
+        sm.getDecomposedLoc(returnTypeCVRange.getBegin());
+    const StringRef file = sm.getBufferData(beg.first);
+    const char *begPtr = file.data() + beg.second;
+
+    const std::pair<FileID, unsigned> end = sm.getDecomposedLoc(
+        Lexer::getLocForEndOfToken(returnTypeCVRange.getEnd(), 0, sm, opts));
+    assert(beg.first == end.first);
+    assert(beg.second != end.second);
+    const char *endPtr = file.data() + end.second;
+
+    return std::string(begPtr, endPtr);
+  }();
+
+  auto d =
+      diag(f->getLocation(), "use a trailing return type for this function");
+  d << FixItHint::CreateReplacement(returnTypeCVRange, "auto");
+  d << FixItHint::CreateInsertion(insertionLoc, " -> " + returnType);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -32,6 +32,7 @@
 #include "UseNoexceptCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
+#include "UseTrailingReturnCheck.h"
 #include "UseTransparentFunctorsCheck.h"
 #include "UseUncaughtExceptionsCheck.h"
 #include "UseUsingCheck.h"
@@ -77,6 +78,8 @@
     CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    CheckFactories.registerCheck<UseTrailingReturnCheck>(
+        "modernize-use-trailing-return");
     CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
         "modernize-use-transparent-functors");
     CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -26,6 +26,7 @@
   UseNoexceptCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
+  UseTrailingReturnCheck.cpp
   UseTransparentFunctorsCheck.cpp
   UseUncaughtExceptionsCheck.cpp
   UseUsingCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to