bernhardmgruber updated this revision to Diff 198143. bernhardmgruber marked 9 inline comments as done. bernhardmgruber added a comment.
It took a long while to figure out how to handle certain macro cases. Here is what I came up with: When tokenizing the source code from the beginning of the function to the function name, I now use clang's `Preprocessor` to lex these tokens again and expand macros on the way. I analyse the top-level macros if they just contain specifiers or qualifiers and store this information in a `ClassifiedToken`. When I later try to expand the return type location to include qualifiers, or when I want to remove specifiers from the return type range, I can use this information to also include/reprosition macros which I can regard as qualifiers or specifiers. This currently solves a lot of cases where macros are part of the return type. Function style macros as part of the return type are not supported, as they are harder to lex and expand. The check currently provides no fixit in these cases. Other changes: - `expandIfMacroId()` now expands recursively because the `SourceLocation` might be inside nested macros - renamed `nonMacroTokensBeforeFunctionName()` into `classifyTokensBeforeFunctionName()` - improved some comments - overriding `registerPPCallbacks` to hijack a reference to the preprocessor - expanding macro ids in the initial return type source range gotten from the `FunctionDecl` CHANGES SINCE LAST ACTION https://reviews.llvm.org/D56160/new/ https://reviews.llvm.org/D56160 Files: clang-tidy/modernize/CMakeLists.txt clang-tidy/modernize/ModernizeTidyModule.cpp clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp clang-tidy/modernize/UseTrailingReturnTypeCheck.h docs/ReleaseNotes.rst docs/clang-tidy/checks/list.rst docs/clang-tidy/checks/modernize-use-trailing-return-type.rst test/clang-tidy/modernize-use-trailing-return-type.cpp
Index: test/clang-tidy/modernize-use-trailing-return-type.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-trailing-return-type.cpp @@ -0,0 +1,563 @@ +// RUN: %check_clang_tidy %s modernize-use-trailing-return-type %t -- -- --std=c++14 -fdeclspec + +namespace std { + template <typename T> + class vector; + + template <typename T, int N> + class array; + + class string; + + template <typename T> + auto declval() -> T; +} + +// +// Functions +// + +int f(); +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto f() -> int;{{$}} +int ((f))(); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// 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-type] +// 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-type] +// 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-type] +// 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-type] +// 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-type] +// CHECK-FIXES: {{^}}template <typename T> auto f(T t) -> int;{{$}} + +// +// Functions with formatting +// + +int a1() { return 42; } +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// 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-type] +// 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-type] +// CHECK-FIXES: {{^}}auto a3() -> int{{$}} +int a4(int arg ) ; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto a4(int arg ) -> int ;{{$}} +int a5 +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto a5{{$}} +(int arg); +// CHECK-FIXES: {{^}}(int arg) -> int;{{$}} +const +int +* +a7 +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +() +// CHECK-FIXES: {{^}}() -> const{{$}} +// CHECK-FIXES: {{^}}int{{$}} +// CHECK-FIXES: {{^}}*{{$}} +; + +int*a7(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto a7(int arg) -> int*;{{$}} +template<template <typename> class C> +C<int>a8(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto a8(int arg) -> C<int>;{{$}} + + +// +// Functions with qualifiers and specifiers +// + +inline int d1(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}inline auto d1(int arg) -> int;{{$}} +extern "C" int d2(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}extern "C" auto d2(int arg) -> int;{{$}} +inline int d3(int arg) noexcept(true); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}inline auto d3(int arg) noexcept(true) -> int;{{$}} +inline int d4(int arg) try { } catch(...) { } +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}inline auto d4(int arg) -> int try { } catch(...) { }{{$}} +int d5(int arg) throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto d5(int arg) throw() -> int;{{$}} +static int d6(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto d6(int arg) -> int;{{$}} +int static d6(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto static d6(int arg) -> int;{{$}} +unsigned static int d7(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto d7(int arg) -> unsigned int;{{$}} +const long static int volatile constexpr unsigned inline long d8(int arg); +// CHECK-MESSAGES: :[[@LINE-1]]:63: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static constexpr inline auto d8(int arg) -> const long int volatile unsigned long;{{$}} +int constexpr d9(); +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto constexpr d9() -> int;{{$}} +inline int constexpr d10(); +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}inline auto constexpr d10() -> int;{{$}} +unsigned constexpr int d11(); +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}constexpr auto d11() -> unsigned int;{{$}} +unsigned extern int d13(); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}extern auto d13() -> unsigned int;{{$}} +int static& d14(); +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto d14() -> int &;{{$}} +class DDD { + int friend unsigned m1(); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} friend auto m1() -> int unsigned;{{$}} + int friend unsigned m1() { return 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} friend auto m1() -> int unsigned { return 0; }{{$}} + const long int friend volatile constexpr unsigned inline long m2(); +// CHECK-MESSAGES: :[[@LINE-1]]:67: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} friend constexpr inline auto m2() -> const long int volatile unsigned long;{{$}} + int virtual unsigned m3(); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} virtual auto m3() -> int unsigned;{{$}} + template <typename T> + int friend unsigned m4(); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} friend auto m4() -> int unsigned;{{$}} +}; + +// +// Functions in namespaces +// + +namespace N { + int e1(); +} +// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto e1() -> int;{{$}} +int N::e1() {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto N::e1() -> int {}{{$}} + +// +// Functions with unsupported return types +// +int (*e3())(double); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}int (*e3())(double);{{$}} +struct A; +int A::* e5(); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}int A::* e5();{{$}} +int std::vector<std::string>::* e6(); +// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}int std::vector<std::string>::* e6();{{$}} +int (std::vector<std::string>::*e7())(double); +// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}int (std::vector<std::string>::*e7())(double);{{$}} + +// +// Functions with complex return types +// + +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-type] +// 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-type] +// 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-type] +// CHECK-FIXES: {{^}}inline auto e2() -> std::vector<std::string> const volatile;{{$}} +int* e8(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto e8() -> int*;{{$}} +static const char* e9(void* user_data); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto e9(void* user_data) -> const char*;{{$}} +static const char* const e10(void* user_data); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto e10(void* user_data) -> const char* const;{{$}} +static const char** volatile * const & e11(void* user_data); +// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto e11(void* user_data) -> const char** volatile * const &;{{$}} +static const char* const * const * const e12(void* user_data); +// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}static auto e12(void* user_data) -> const char* const * const * const;{{$}} +struct A e13(); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto e13() -> struct A;{{$}} + +// +// decltype (unsupported if top level expression) +// + +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; }{{$}} +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); }{{$}} +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] +// CHECK-FIXES: {{^}}auto dec3() -> typename decltype(std::declval<T>())::value_type;{{$}} +template <typename T> +decltype(std::declval<T>())* dec4(); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto dec4() -> decltype(std::declval<T>())*;{{$}} + +// +// Methods +// + +struct B { + B& operator=(const B&); +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto operator=(const B&) -> B&;{{$}} + + double base1(int, bool b); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// 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-type] +// 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-type] +// CHECK-FIXES: {{^}} virtual auto base3() const -> float = 0;{{$}} + + virtual float base4() volatile = 0; +// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} virtual auto base4() volatile -> float = 0;{{$}} + + double base5(int, bool b) &&; +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto base5(int, bool b) && -> double;{{$}} + + double base6(int, bool b) const &&; +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto base6(int, bool b) const && -> double;{{$}} + + double base7(int, bool b) const & = delete; +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto base7(int, bool b) const & -> double = delete;{{$}} + + double base8(int, bool b) const volatile & = delete; +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto base8(int, bool b) const volatile & -> double = delete;{{$}} + + virtual const char * base9() const noexcept { return ""; } +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} virtual auto base9() const noexcept -> const char * { return ""; }{{$}} +}; + +double B::base1(int, bool b) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto B::base1(int, bool b) -> double {}{{$}} + +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-type] +// 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-type] +// 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-type] +// CHECK-FIXES: {{^}} virtual auto base3() const -> float override final { }{{$}} + + const char * base9() const noexcept override { return ""; } +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto base9() const noexcept -> const char * override { return ""; }{{$}} + + int f2() __restrict; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto f2() __restrict -> int;{{$}} + + volatile int* __restrict f3() const __restrict noexcept; +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto f3() const __restrict noexcept -> volatile int* __restrict;{{$}} +}; + +// +// Functions with attributes +// + +int g1() [[asdf]]; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto g1() -> int {{[[][[]}}asdf{{[]][]]}};{{$}} +[[noreturn]] int g2(); +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}{{[[][[]}}noreturn{{[]][]]}} auto g2() -> int;{{$}} +int g2 [[noreturn]] (); +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto g2 {{[[][[]}}noreturn{{[]][]]}} () -> int;{{$}} +int unsigned g3() __attribute__((cdecl)); // FunctionTypeLoc is null. +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +int unsigned __attribute__((cdecl)) g3() ; // FunctionTypeLoc is null. +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +__attribute__((cdecl)) int unsigned g3() ; // FunctionTypeLoc is null. +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use a trailing return type for this function [modernize-use-trailing-return-type] + +// +// Templates +// +template <typename Container> +[[maybe_unused]] typename Container::value_type const volatile&& t1(Container& C) noexcept; +// CHECK-MESSAGES: :[[@LINE-1]]:66: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}{{[[][[]}}maybe_unused{{[]][]]}} auto t1(Container& C) noexcept -> typename Container::value_type const volatile&&;{{$}} +template <typename T> +class BB { + using type = int; + + template <typename U> + typename BB<U>::type m1(); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto m1() -> typename BB<U>::type;{{$}} +}; + +// +// Macros +// + +#define DWORD unsigned int +DWORD h1(); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h1() -> DWORD;{{$}} +#define INT int +#define UNSIGNED unsigned +UNSIGNED INT h2(); +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h2() -> UNSIGNED INT;{{$}} +#define CONST const +CONST int h3(); +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h3() -> CONST int;{{$}} +#define ALWAYS_INLINE inline +#define DLL_EXPORT __declspec(dllexport) +ALWAYS_INLINE DLL_EXPORT int h4(); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}ALWAYS_INLINE DLL_EXPORT auto h4() -> int;{{$}} +#define DEPRECATED __attribute__((deprecated)) +int h5() DEPRECATED; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h5() -> int DEPRECATED;{{$}} +int DEPRECATED h5(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto DEPRECATED h5() -> int;{{$}} +DEPRECATED int h5(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED auto h5() -> int;{{$}} +[[noreturn]] [[nodiscard]] DEPRECATED DLL_EXPORT int h6 [[deprecated]] (); +// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}{{[[][[]}}noreturn{{[]][]]}} {{[[][[]}}nodiscard{{[]][]]}} DEPRECATED DLL_EXPORT auto h6 {{[[][[]}}deprecated{{[]][]]}} () -> int;{{$}} +#define FUNCTION_NAME(a, b) a##b +int FUNCTION_NAME(foo, bar)(); +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto FUNCTION_NAME(foo, bar)() -> int;{{$}} +#define DEFINE_FUNCTION_1(a, b) int a##b() +DEFINE_FUNCTION_1(foo, bar); +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define DEFINE_FUNCTION_2 int foo(int arg); +DEFINE_FUNCTION_2 +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define DLL_EXPORT_const __declspec(dllexport) const +DLL_EXPORT_const int h7(); +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define DLL_EXPORT_CONST __declspec(dllexport) CONST +DLL_EXPORT_CONST int h7(); +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use a trailing return type for this function [modernize-use-trailing-return-type] + +template <typename T> +using Real = T; +#define PRECISION float +Real<PRECISION> h8() { return 0.; } +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h8() -> Real<PRECISION> { return 0.; }{{$}} + +#define MAYBE_UNUSED_MACRO [[maybe_unused]] +template <typename Container> +MAYBE_UNUSED_MACRO typename Container::value_type const volatile** const h9(Container& C) noexcept; +// CHECK-MESSAGES: :[[@LINE-1]]:74: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}MAYBE_UNUSED_MACRO auto h9(Container& C) noexcept -> typename Container::value_type const volatile** const;{{$}} + +#define NOEXCEPT noexcept +int h9(int arg) NOEXCEPT; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h9(int arg) NOEXCEPT -> int;{{$}} +#define STATIC_INT static int +STATIC_INT h10(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define UNSIGNED_STATIC_INT unsigned static int +UNSIGNED_STATIC_INT h11(); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define STATIC static +unsigned STATIC int h11(); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}STATIC auto h11() -> unsigned int;{{$}} +#define STATIC_CONSTEXPR static constexpr +unsigned STATIC_CONSTEXPR int h12(); +// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}STATIC_CONSTEXPR auto h12() -> unsigned int;{{$}} +#define STATIC_CONSTEXPR_LONG static constexpr long +unsigned STATIC_CONSTEXPR_LONG int h13(); +// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +DEPRECATED const int& h14(); +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED auto h14() -> const int&;{{$}} +DEPRECATED const long static volatile unsigned& h15(); +// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED static auto h15() -> const long volatile unsigned&;{{$}} +#define WRAP(x) x +WRAP(const) int& h16(); +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +WRAP(CONST) int& h16(); +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +#define CONCAT(a, b) a##b +CONCAT(con, st) int& h16(); +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +DEPRECATED const UNSIGNED& h17(); +// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED auto h17() -> const UNSIGNED&;{{$}} +DEPRECATED CONST UNSIGNED STATIC& h17(); +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED STATIC auto h17() -> CONST UNSIGNED &;{{$}} +#define CONST_CAT con##st +CONST_CAT int& h18(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h18() -> CONST_CAT int&;{{$}} +#define CONST_F_MACRO WRAP(CONST_CAT) +CONST_F_MACRO int& h18(); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h18() -> CONST_F_MACRO int&;{{$}} + +// +// Name collisions +// +struct Object { long long value; }; + +Object j1(unsigned Object) { return {Object * 2}; } +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}Object j1(unsigned Object) { return {Object * 2}; }{{$}} +::Object j1(unsigned Object); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j1(unsigned Object) -> ::Object;{{$}} +const Object& j2(unsigned a, int b, char Object, long l); +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}const Object& j2(unsigned a, int b, char Object, long l);{{$}} +const struct Object& j2(unsigned a, int b, char Object, long l); +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j2(unsigned a, int b, char Object, long l) -> const struct Object&;{{$}} +std::vector<Object> j3(unsigned Object); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}std::vector<Object> j3(unsigned Object);{{$}} +std::vector<const Object> j7(unsigned Object); +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}std::vector<const Object> j7(unsigned Object);{{$}} +std::vector<Object> j4(unsigned vector); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j4(unsigned vector) -> std::vector<Object>;{{$}} +std::vector<::Object> j4(unsigned vector); +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j4(unsigned vector) -> std::vector<::Object>;{{$}} +std::vector<struct Object> j4(unsigned vector); +// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j4(unsigned vector) -> std::vector<struct Object>;{{$}} +std::vector<Object> j4(unsigned Vector); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto j4(unsigned Vector) -> std::vector<Object>;{{$}} +using std::vector; +vector<Object> j5(unsigned vector); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}vector<Object> j5(unsigned vector);{{$}} +constexpr auto Size = 5; +std::array<int, Size> j6(unsigned Size); +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}std::array<int, Size> j6(unsigned Size);{{$}} +std::array<decltype(Size), (Size * 2) + 1> j8(unsigned Size); +// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}std::array<decltype(Size), (Size * 2) + 1> j8(unsigned Size);{{$}} + +class CC { + int Object; + struct Object m(); +// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto m() -> struct Object;{{$}} +}; +Object CC::m() { return {0}; } +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto CC::m() -> Object { return {0}; }{{$}} +class DD : public CC { + ::Object g(); +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}} auto g() -> ::Object;{{$}} +}; +Object DD::g() { +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto DD::g() -> Object {{{$}} + return {0}; +} + + +// +// Samples which do not trigger the check +// + +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; + +auto ff(); +decltype(auto) fff(); + +void c(); +void c(int arg); +void c(int arg) { return; } + +struct D2 : B { + D2(); + virtual ~D2(); + + 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(); +}; + +auto l1 = [](int arg) {}; +auto l2 = [](int arg) -> double {}; Index: docs/clang-tidy/checks/modernize-use-trailing-return-type.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-trailing-return-type.rst @@ -0,0 +1,68 @@ +.. title:: clang-tidy - modernize-use-trailing-return-type + +modernize-use-trailing-return-type +================================== + +Rewrites function signatures to use a trailing return type +(introduced in C++11). This transformation is purely stylistic. +The return type before the function name is replaced by ``auto`` +and inserted after the function parameter list (and qualifiers). + +Example +------- + +.. code-block:: c++ + + int f1(); + inline int f2(int arg) noexcept; + virtual float f3() const && = delete; + +transforms to: + +.. code-block:: c++ + + auto f1() -> int; + inline auto f2(int arg) -> int noexcept; + virtual auto f3() const && -> float = delete; + +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 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. +This location includes e.g. function parameter names and members of the enclosing class (including all inherited classes). +Such a lookup is currently not implemented. + +Given the following piece of code + +.. code-block:: c++ + + struct Object { long long value; }; + Object f(unsigned Object) { return {Object * 2}; } + class CC { + int Object; + struct Object m(); + }; + Object 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 + class CC { + int Object; + auto m() -> struct Object; + }; + auto CC::m() -> Object { 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. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -219,6 +219,7 @@ modernize-use-noexcept modernize-use-nullptr modernize-use-override + modernize-use-trailing-return-type modernize-use-transparent-functors modernize-use-uncaught-exceptions modernize-use-using Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -151,6 +151,14 @@ finds and replaces cases that match the pattern ``var && isa<X>(var)``, where ``var`` is evaluated twice. +- New :doc:`modernize-use-trailing-type-return + <clang-tidy/checks/modernize-use-trailing-type-return>` check. + + Rewrites function signatures to use a trailing return type. + +Improvements to include-fixer +----------------------------- + - New :doc:`openmp-exception-escape <clang-tidy/checks/openmp-exception-escape>` check. Index: clang-tidy/modernize/UseTrailingReturnTypeCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseTrailingReturnTypeCheck.h @@ -0,0 +1,62 @@ +//===--- UseTrailingReturnTypeCheck.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_USETRAILINGRETURNTYPECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USETRAILINGRETURNTYPECHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +struct ClassifiedToken { + Token T; + bool isQualifier; + bool isSpecifier; +}; + +/// 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-type-return.html +class UseTrailingReturnTypeCheck : public ClangTidyCheck { +public: + UseTrailingReturnTypeCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + Preprocessor *PP = nullptr; + + SourceLocation findTrailingReturnTypeSourceLocation( + const FunctionDecl &F, const FunctionTypeLoc &FTL, const ASTContext &Ctx, + const SourceManager &SM, const LangOptions &LangOpts); + llvm::Optional<SmallVector<ClassifiedToken, 8>> + classifyTokensBeforeFunctionName(const FunctionDecl &F, const ASTContext &Ctx, + const SourceManager &SM, + const LangOptions &LangOpts); + SourceRange findReturnTypeAndCVSourceRange(const FunctionDecl &F, + const ASTContext &Ctx, + const SourceManager &SM, + const LangOptions &LangOpts); + bool keepSpecifiers(std::string &ReturnType, std::string &Auto, + SourceRange ReturnTypeCVRange, const FunctionDecl &F, + const FriendDecl *Fr, const ASTContext &Ctx, + const SourceManager &SM, const LangOptions &LangOpts); +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USETRAILINGRETURNTYPECHECK_H Index: clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp @@ -0,0 +1,482 @@ +//===--- UseTrailingReturnTypeCheck.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 "UseTrailingReturnTypeCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/FixIt.h" + +#include <cctype> + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { +namespace { +struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> { +public: + UnqualNameVisitor(const FunctionDecl &F, const SourceManager &SM) + : F(F), SM(SM) {} + + bool Collision = false; + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitUnqualName(StringRef UnqualName) { + // Check for collisions with function arguments. + for (ParmVarDecl *Param : F.parameters()) + if (const IdentifierInfo *Ident = Param->getIdentifier()) + if (Ident->getName() == UnqualName) { + Collision = true; + return true; + } + return false; + } + + bool TraverseTypeLoc(TypeLoc TL, bool Elaborated = false) { + if (TL.isNull()) + return true; + + if (!Elaborated) { + switch (TL.getTypeLocClass()) { + case TypeLoc::Record: + if (VisitUnqualName( + TL.getAs<RecordTypeLoc>().getTypePtr()->getDecl()->getName())) + return false; + break; + case TypeLoc::Enum: + if (VisitUnqualName( + TL.getAs<EnumTypeLoc>().getTypePtr()->getDecl()->getName())) + return false; + break; + case TypeLoc::TemplateSpecialization: + if (VisitUnqualName(TL.getAs<TemplateSpecializationTypeLoc>() + .getTypePtr() + ->getTemplateName() + .getAsTemplateDecl() + ->getName())) + return false; + break; + default: + break; + } + } + + return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(TL); + } + + // Replace the base method in order to call ower own + // TraverseTypeLoc(). + bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) { + return TraverseTypeLoc(TL.getUnqualifiedLoc()); + } + + // Replace the base version to inform TraverseTypeLoc that the type is + // elaborated. + bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) { + if (TL.getQualifierLoc()) + if (!TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())) + return false; + if (!TraverseTypeLoc(TL.getNamedTypeLoc(), true)) + return false; + return true; + } + + bool VisitDeclRefExpr(DeclRefExpr *S) { + DeclarationName Name = S->getNameInfo().getName(); + if (!S->getQualifierLoc() && Name.isIdentifier() && + VisitUnqualName(Name.getAsIdentifierInfo()->getName())) + return false; + return true; + } + +private: + const FunctionDecl &F; + const SourceManager &SM; +}; +} // namespace + +constexpr llvm::StringLiteral Message = + "use a trailing return type for this function"; + +static SourceLocation expandIfMacroId(SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isMacroID()) + Loc = expandIfMacroId(SM.getImmediateExpansionRange(Loc).getBegin(), SM); + assert(!Loc.isMacroID() && + "SourceLocation must not be a macro ID after recursive expansion"); + return Loc; +} + +SourceLocation UseTrailingReturnTypeCheck::findTrailingReturnTypeSourceLocation( + const FunctionDecl &F, const FunctionTypeLoc &FTL, const ASTContext &Ctx, + const SourceManager &SM, const LangOptions &LangOpts) { + // We start with the location of the closing parenthesis. + SourceRange ExceptionSpecRange = F.getExceptionSpecSourceRange(); + if (ExceptionSpecRange.isValid()) + return Lexer::getLocForEndOfToken(ExceptionSpecRange.getEnd(), 0, SM, + LangOpts); + + // If the function argument list ends inside of a macro, it is dangerous to + // start lexing from here - bail out. + SourceLocation ClosingParen = FTL.getRParenLoc(); + if (ClosingParen.isMacroID()) + return {}; + + SourceLocation Result = + Lexer::getLocForEndOfToken(ClosingParen, 0, SM, LangOpts); + + // Skip subsequent CV and ref qualifiers. + std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(Result); + 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; + while (!Lexer.LexFromRawLexer(T)) { + 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.isOneOf(tok::amp, tok::ampamp, tok::kw_const, tok::kw_volatile, + tok::kw_restrict)) { + Result = T.getEndLoc(); + continue; + } + break; + } + return Result; +} + +static bool IsCVR(Token T) { + return T.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict); +} + +static bool IsSpecifier(Token T) { + return T.isOneOf(tok::kw_constexpr, tok::kw_inline, tok::kw_extern, + tok::kw_static, tok::kw_friend, tok::kw_virtual); +} + +static llvm::Optional<ClassifiedToken> +classifyToken(const FunctionDecl &F, Preprocessor &PP, Token Tok) { + ClassifiedToken CT; + CT.T = Tok; + CT.isQualifier = true; + CT.isSpecifier = true; + bool ContainsQualifiers = false; + bool ContainsSpecifiers = false; + bool ContainsSomethingElse = false; + + Token End; + End.setKind(tok::eof); + SmallVector<Token, 2> Stream{Tok, End}; + PP.EnterTokenStream(Stream, false); + while (true) { + Token T; + PP.Lex(T); + if (T.is(tok::eof)) + break; + + bool Qual = IsCVR(T); + bool Spec = IsSpecifier(T); + CT.isQualifier &= Qual; + CT.isSpecifier &= Spec; + ContainsQualifiers |= Qual; + ContainsSpecifiers |= Spec; + ContainsSomethingElse |= !Qual && !Spec; + } + + // If the Token/Macro contains more than one type of tokens, we would need + // to split the macro in order to move parts to the trailing return type. + if (ContainsQualifiers + ContainsSpecifiers + ContainsSomethingElse > 1) + return {}; + + return CT; +} + +llvm::Optional<SmallVector<ClassifiedToken, 8>> +UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName( + const FunctionDecl &F, const ASTContext &Ctx, const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation BeginF = expandIfMacroId(F.getBeginLoc(), SM); + SourceLocation BeginNameF = expandIfMacroId(F.getLocation(), SM); + + // Create tokens for everything before the name of the function. + std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(BeginF); + 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; + SmallVector<ClassifiedToken, 8> ClassifiedTokens; + while (!Lexer.LexFromRawLexer(T) && + SM.isBeforeInTranslationUnit(T.getLocation(), BeginNameF)) { + if (T.is(tok::raw_identifier)) { + IdentifierInfo &Info = Ctx.Idents.get( + StringRef(SM.getCharacterData(T.getLocation()), T.getLength())); + + if (Info.hasMacroDefinition()) { + const MacroInfo *MI = PP->getMacroInfo(&Info); + if (!MI || MI->isFunctionLike()) { + // Cannot handle function style macros. + diag(F.getLocation(), Message); + return {}; + } + } + + T.setIdentifierInfo(&Info); + T.setKind(Info.getTokenID()); + } + + if (llvm::Optional<ClassifiedToken> CT = classifyToken(F, *PP, T)) + ClassifiedTokens.push_back(*CT); + else { + diag(F.getLocation(), Message); + return {}; + } + } + + return ClassifiedTokens; +} + +static bool hasAnyNestedLocalQualifiers(QualType Type) { + bool Result = Type.hasLocalQualifiers(); + if (Type->isPointerType()) + Result = Result || hasAnyNestedLocalQualifiers( + Type->castAs<PointerType>()->getPointeeType()); + if (Type->isReferenceType()) + Result = Result || hasAnyNestedLocalQualifiers( + Type->castAs<ReferenceType>()->getPointeeType()); + return Result; +} + +SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange( + 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 + // qualifiers (const, volatile and restrict). + SourceRange ReturnTypeRange = F.getReturnTypeSourceRange(); + if (ReturnTypeRange.isInvalid()) { + // Happens if e.g. clang cannot resolve all includes and the return type is + // unknown. + diag(F.getLocation(), Message); + return {}; + } + + // If the return type has no local qualifiers, it's source range is accurate. + if (!hasAnyNestedLocalQualifiers(F.getReturnType())) + return ReturnTypeRange; + + // Include qualifiers to the left and right of the return type. + llvm::Optional<SmallVector<ClassifiedToken, 8>> MaybeTokens = + classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts); + if (!MaybeTokens) + return {}; + const SmallVector<ClassifiedToken, 8> &Tokens = *MaybeTokens; + + ReturnTypeRange.setBegin(expandIfMacroId(ReturnTypeRange.getBegin(), SM)); + ReturnTypeRange.setEnd(expandIfMacroId(ReturnTypeRange.getEnd(), SM)); + + bool ExtendedLeft = false; + for (size_t I = 0; I < Tokens.size(); I++) { + // If we found the beginning of the return type, include left qualifiers. + if (!SM.isBeforeInTranslationUnit(Tokens[I].T.getLocation(), + ReturnTypeRange.getBegin()) && + !ExtendedLeft) { + assert(I <= size_t(std::numeric_limits<int>::max()) && + "Integer overflow detected"); + for (int J = static_cast<int>(I) - 1; J >= 0 && Tokens[J].isQualifier; + J--) + ReturnTypeRange.setBegin(Tokens[J].T.getLocation()); + ExtendedLeft = true; + } + // If we found the end of the return type, include right qualifiers. + if (SM.isBeforeInTranslationUnit(ReturnTypeRange.getEnd(), + Tokens[I].T.getLocation())) { + for (size_t J = I; J < Tokens.size() && Tokens[J].isQualifier; J++) + ReturnTypeRange.setEnd(Tokens[J].T.getLocation()); + break; + } + } + + assert(!ReturnTypeRange.getBegin().isMacroID() && + "Return type source range begin must not be a macro"); + assert(!ReturnTypeRange.getEnd().isMacroID() && + "Return type source range end must not be a macro"); + return ReturnTypeRange; +} + +bool 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) { + // Check if there are specifiers inside the return type. E.g. unsigned + // inline int. + const auto *M = dyn_cast<CXXMethodDecl>(&F); + if (!F.isConstexpr() && !F.isInlineSpecified() && + F.getStorageClass() != SC_Extern && F.getStorageClass() != SC_Static && + !Fr && !(M && M->isVirtualAsWritten())) + return true; + + // 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; + + // Find specifiers, remove them from the return type, add them to 'auto'. + unsigned int ReturnTypeBeginOffset = + SM.getDecomposedLoc(ReturnTypeCVRange.getBegin()).second; + size_t InitialAutoLength = Auto.size(); + unsigned int DeletedChars = 0; + for (ClassifiedToken CT : *MaybeTokens) { + if (SM.isBeforeInTranslationUnit(CT.T.getLocation(), + ReturnTypeCVRange.getBegin()) || + SM.isBeforeInTranslationUnit(ReturnTypeCVRange.getEnd(), + CT.T.getLocation())) + continue; + if (!CT.isSpecifier) + continue; + + // Add the token to 'auto' and remove it from the return type, including + // any whitespace following the token. + unsigned int TOffset = SM.getDecomposedLoc(CT.T.getLocation()).second; + assert(TOffset >= ReturnTypeBeginOffset && + "Token location must be after the beginning of the return type"); + unsigned int TOffsetInRT = TOffset - ReturnTypeBeginOffset - DeletedChars; + unsigned int TLengthWithWS = CT.T.getLength(); + while (TOffsetInRT + TLengthWithWS < ReturnType.size() && + std::isspace(ReturnType[TOffsetInRT + TLengthWithWS])) + TLengthWithWS++; + std::string Specifier = ReturnType.substr(TOffsetInRT, TLengthWithWS); + if (!std::isspace(Specifier.back())) + Specifier.push_back(' '); + Auto.insert(Auto.size() - InitialAutoLength, Specifier); + ReturnType.erase(TOffsetInRT, TLengthWithWS); + DeletedChars += TLengthWithWS; + } + + return true; +} + +void UseTrailingReturnTypeCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + + auto F = functionDecl(unless(anyOf(hasTrailingReturn(), returns(voidType()), + returns(autoType()), cxxConversionDecl(), + cxxMethodDecl(isImplicit())))) + .bind("Func"); + + Finder->addMatcher(F, this); + Finder->addMatcher(friendDecl(hasDescendant(F)).bind("Friend"), this); +} + +void UseTrailingReturnTypeCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + this->PP = PP; +} + +void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) { + assert(PP && "Expected registerPPCallbacks() to have been called before so " + "preprocessor is available"); + + const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("Func"); + const auto *Fr = Result.Nodes.getNodeAs<FriendDecl>("Friend"); + assert(F && "Matcher is expected to find only FunctionDecls"); + + if (F->getLocation().isInvalid()) + return; + + // TODO: implement those + if (F->getDeclaredReturnType()->isFunctionPointerType() || + F->getDeclaredReturnType()->isMemberFunctionPointerType() || + F->getDeclaredReturnType()->isMemberPointerType() || + F->getDeclaredReturnType()->getAs<DecltypeType>() != nullptr) { + diag(F->getLocation(), Message); + return; + } + + const ASTContext &Ctx = *Result.Context; + const SourceManager &SM = *Result.SourceManager; + const LangOptions &LangOpts = getLangOpts(); + + const TypeSourceInfo *TSI = F->getTypeSourceInfo(); + if (!TSI) + return; + + FunctionTypeLoc FTL = + TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>(); + if (!FTL) { + // FIXME: This may happen if we have __attribute__((...)) on the function. + // We abort for now. Remove this when the function type location gets + // available in clang. + diag(F->getLocation(), Message); + return; + } + + SourceLocation InsertionLoc = + findTrailingReturnTypeSourceLocation(*F, FTL, Ctx, SM, LangOpts); + if (InsertionLoc.isInvalid()) { + diag(F->getLocation(), Message); + return; + } + + // Using the declared return type via F->getDeclaredReturnType().getAsString() + // discards user formatting and order of const, volatile, type, whitespace, + // space before & ... . + SourceRange ReturnTypeCVRange = + findReturnTypeAndCVSourceRange(*F, Ctx, SM, LangOpts); + if (ReturnTypeCVRange.isInvalid()) + return; + + // Check if unqualified names in the return type conflict with other entities + // after the rewrite. + // FIXME: this could be done better, by performing a lookup of all + // unqualified names in the return type in the scope of the function. If the + // lookup finds a different entity than the original entity identified by the + // name, then we can either not perform a rewrite or explicitely qualify the + // entity. Such entities could be function parameter names, (inherited) class + // members, template parameters, etc. + UnqualNameVisitor UNV{*F, SM}; + UNV.TraverseTypeLoc(FTL.getReturnLoc()); + if (UNV.Collision) { + diag(F->getLocation(), Message); + return; + } + + SourceLocation ReturnTypeEnd = + Lexer::getLocForEndOfToken(ReturnTypeCVRange.getEnd(), 0, SM, LangOpts); + StringRef CharAfterReturnType = Lexer::getSourceText( + CharSourceRange::getCharRange(ReturnTypeEnd, + ReturnTypeEnd.getLocWithOffset(1)), + SM, LangOpts); + bool NeedSpaceAfterAuto = + CharAfterReturnType.empty() || !std::isspace(CharAfterReturnType[0]); + + std::string Auto = NeedSpaceAfterAuto ? "auto " : "auto"; + std::string ReturnType = tooling::fixit::getText(ReturnTypeCVRange, Ctx); + keepSpecifiers(ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM, + LangOpts); + + diag(F->getLocation(), Message) + << FixItHint::CreateReplacement(ReturnTypeCVRange, Auto) + << 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 @@ -35,6 +35,7 @@ #include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" +#include "UseTrailingReturnTypeCheck.h" #include "UseTransparentFunctorsCheck.h" #include "UseUncaughtExceptionsCheck.h" #include "UseUsingCheck.h" @@ -87,6 +88,8 @@ CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept"); CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr"); CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override"); + CheckFactories.registerCheck<UseTrailingReturnTypeCheck>( + "modernize-use-trailing-return-type"); 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 @@ -30,6 +30,7 @@ UseNoexceptCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp + UseTrailingReturnTypeCheck.cpp UseTransparentFunctorsCheck.cpp UseUncaughtExceptionsCheck.cpp UseUsingCheck.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits