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 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("modernize-use-noexcept"); CheckFactories.registerCheck("modernize-use-nullptr"); CheckFactories.registerCheck("modernize-use-override"); + CheckFactories.registerCheck( + "modernize-use-trailing-return"); CheckFactories.registerCheck( "modernize-use-transparent-functors"); CheckFactories.registerCheck( 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 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 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 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("f"); + if (!f) + return; + + if (const auto *m = dyn_cast(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 beg = + sm.getDecomposedLoc(returnTypeCVRange.getBegin()); + const StringRef file = sm.getBufferData(beg.first); + const char *begPtr = file.data() + beg.second; + + const std::pair 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: 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: 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 + 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 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 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 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;{{$}} +inline const std::vector 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 volatile;{{$}} +inline std::vector 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 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 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(); +};