bernhardmgruber updated this revision to Diff 198937. bernhardmgruber marked 3 inline comments as done. bernhardmgruber added a comment.
- fixed formatting - fixed function names in tests - added `-fexceptions` to test arguments - fixed type in release notes 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 -fexceptions + +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& h18(); +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}DEPRECATED STATIC auto h18() -> CONST UNSIGNED &;{{$}} +#define CONST_CAT con##st +CONST_CAT int& h19(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h19() -> CONST_CAT int&;{{$}} +#define CONST_F_MACRO WRAP(CONST_CAT) +CONST_F_MACRO int& h19(); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type] +// CHECK-FIXES: {{^}}auto h19() -> CONST_F_MACRO int&;{{$}} + +// +// 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-return-type + <clang-tidy/checks/modernize-use-trailing-return-type>` 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,478 @@ +//===--- 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() && + !TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())) + return false; + return TraverseTypeLoc(TL.getNamedTypeLoc(), true); + } + + bool VisitDeclRefExpr(DeclRefExpr *S) { + DeclarationName Name = S->getNameInfo().getName(); + return S->getQualifierLoc() || !Name.isIdentifier() || + !VisitUnqualName(Name.getAsIdentifierInfo()->getName()); + } + +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 llvm::None; + + 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 llvm::None; + } + } + + 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 llvm::None; + } + } + + 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