AMS21 updated this revision to Diff 526327. AMS21 added a comment. Removed unrelated change
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D148697/new/ https://reviews.llvm.org/D148697 Files: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp clang-tools-extra/clang-tidy/performance/CMakeLists.txt clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.cpp clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.h clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.cpp clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.h clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.cpp clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.h clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp clang-tools-extra/clang-tidy/utils/LexerUtils.cpp clang-tools-extra/clang-tidy/utils/LexerUtils.h clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-destructor.rst clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-move-operations.rst clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-swap.rst clang-tools-extra/docs/clang-tidy/checks/list.rst clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-destructor.rst clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-swap.rst clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-destructor.cpp clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-swap.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-swap.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-swap.cpp @@ -0,0 +1,203 @@ +// RUN: %check_clang_tidy %s performance-noexcept-swap %t -- -- -fexceptions + +void throwing_function() noexcept(false); +void noexcept_function() noexcept; + +template <typename> +struct TemplateNoexceptWithInt { + static void f() {} +}; + +template <> +struct TemplateNoexceptWithInt<int> { + static void f() noexcept {} +}; + +class A { + void swap(A &); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: swap functions should be marked noexcept [performance-noexcept-swap] + // CHECK-FIXES: void swap(A &) noexcept ; +}; + +void swap(A &, A &); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: swap functions should be marked noexcept [performance-noexcept-swap] +// CHECK-FIXES: void swap(A &, A &) noexcept ; + +struct B { + static constexpr bool kFalse = false; + void swap(B &) noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +}; + +void swap(B &, B &) noexcept(B::kFalse); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] + +template <typename> +struct C { + void swap(C&); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: swap functions should be marked noexcept [performance-noexcept-swap] + // CHECK-FIXES: void swap(C&) noexcept ; +}; + +template <typename T> +void swap(C<T>&, C<T>&); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: swap functions should be marked noexcept [performance-noexcept-swap] +// CHECK-FIXES: void swap(C<T>&, C<T>&) noexcept ; +void swap(C<int>&, C<int>&); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: swap functions should be marked noexcept [performance-noexcept-swap] +// CHECK-FIXES: void swap(C<int>&, C<int>&) noexcept ; + +template <typename> +struct D { + static constexpr bool kFalse = false; + void swap(D &) noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +}; + +template <typename T> +void swap(D<T> &, D<T> &) noexcept(D<T>::kFalse); +// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +void swap(D<int> &, D<int> &) noexcept(D<int>::kFalse); +// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] + +struct E { + void swap(E &) noexcept(noexcept(throwing_function())); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +}; + +void swap(E &, E &) noexcept(noexcept(throwing_function())); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] + +template <typename> +struct F { + void swap(F &) noexcept(noexcept(throwing_function())); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +}; + +template <typename T> +void swap(F<T> &, F<T> &) noexcept(noexcept(throwing_function())); +// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +void swap(F<int> &, F<int> &) noexcept(noexcept(throwing_function())); +// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] + +struct G { + void swap(G &) noexcept(noexcept(TemplateNoexceptWithInt<double>::f())); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] +}; + +void swap(G &, G &) noexcept(noexcept(TemplateNoexceptWithInt<double>::f())); +// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap] + +class OK {}; + +struct OK1 { + void swap(OK1 &) noexcept; +}; + +void swap(OK1 &, OK1 &) noexcept; + +struct OK2 { + static constexpr bool kTrue = true; + void swap(OK2 &) noexcept(kTrue) {} +}; + +void swap(OK2 &, OK2 &) noexcept(OK2::kTrue); + +struct OK3 { + void swap(OK3 &) = delete; +}; + +void swap(OK3 &, OK3 &) = delete; + +struct OK4 { + void swap(OK4 &) noexcept(false); +}; + +void swap(OK4 &, OK4 &) noexcept(false); + +struct OK5 { + void swap(OK5 &) noexcept(true); +}; + +void swap(OK5 &, OK5 &)noexcept(true); + +struct OK12 { + void swap(OK12 &) noexcept(noexcept(noexcept_function())); +}; + +void swap(OK12 &, OK12 &) noexcept(noexcept(noexcept_function())); + +struct OK13 { + void swap(OK13 &) noexcept(noexcept(TemplateNoexceptWithInt<int>::f())); +}; + +void swap(OK13 &, OK13 &) noexcept(noexcept(TemplateNoexceptWithInt<int>::f())); + +template <typename> +class OK14 {}; + +template <typename> +struct OK15 { + void swap(OK15 &) noexcept; +}; + +template <typename T> +void swap(OK15<T> &, OK15<T> &) noexcept; +void swap(OK15<int> &, OK15<int> &) noexcept; + +template <typename> +struct OK16 { + static constexpr bool kTrue = true; + void swap(OK16 &) noexcept(kTrue); +}; + +// FIXME: This gives a warning, but it should be OK. +//template <typename T> +//void swap(OK16<T> &, OK16<T> &) noexcept(OK16<T>::kTrue); +template <typename T> +void swap(OK16<int> &, OK16<int> &) noexcept(OK16<int>::kTrue); + +template <typename> +struct OK17 { + void swap(OK17 &) = delete; +}; + +template <typename T> +void swap(OK17<T> &, OK17<T> &) = delete; +void swap(OK17<int> &, OK17<int> &) = delete; + +template <typename> +struct OK18 { + void swap(OK18 &) noexcept(false); +}; + +template <typename T> +void swap(OK18<T> &, OK18<T> &) noexcept(false); +void swap(OK18<int> &, OK18<int> &) noexcept(false); + +template <typename> +struct OK19 { + void swap(OK19 &) noexcept(true); +}; + +template <typename T> +void swap(OK19<T> &, OK19<T> &)noexcept(true); +void swap(OK19<int> &, OK19<int> &)noexcept(true); + +template <typename> +struct OK20 { + void swap(OK20 &) noexcept(noexcept(noexcept_function())); +}; + +template <typename T> +void swap(OK20<T> &, OK20<T> &) noexcept(noexcept(noexcept_function())); +void swap(OK20<int> &, OK20<int> &) noexcept(noexcept(noexcept_function())); + +template <typename> +struct OK21 { + void swap(OK21 &) noexcept(noexcept(TemplateNoexceptWithInt<int>::f())); +}; + +template <typename T> +void swap(OK21<T> &, OK21<T> &) noexcept(noexcept(TemplateNoexceptWithInt<int>::f())); +void swap(OK21<int> &, OK21<int> &) noexcept(noexcept(TemplateNoexceptWithInt<int>::f())); Index: clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp +++ clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp @@ -17,50 +17,50 @@ static constexpr bool value = true; }; -struct ThrowingMoveConstructor -{ - ThrowingMoveConstructor() = default; - ThrowingMoveConstructor(ThrowingMoveConstructor&&) noexcept(false) { - } - ThrowingMoveConstructor& operator=(ThrowingMoveConstructor &&) noexcept(false) { - return *this; - } +struct ThrowOnAnything { + ThrowOnAnything() noexcept(false); + ThrowOnAnything(ThrowOnAnything&&) noexcept(false); + ThrowOnAnything& operator=(ThrowOnAnything &&) noexcept(false); + ~ThrowOnAnything() noexcept(false); }; class A { A(A &&); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: A(A &&) noexcept ; A &operator=(A &&); // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: A &operator=(A &&) noexcept ; }; struct B { static constexpr bool kFalse = false; B(B &&) noexcept(kFalse); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] + B &operator=(B &&) noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; template <typename> -struct C -{ +struct C { C(C &&); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: C(C &&) noexcept ; C& operator=(C &&); // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: C& operator=(C &&) noexcept ; }; -struct D -{ +struct D { static constexpr bool kFalse = false; D(D &&) noexcept(kFalse) = default; // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] D& operator=(D &&) noexcept(kFalse) = default; - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; template <typename> -struct E -{ +struct E { static constexpr bool kFalse = false; E(E &&) noexcept(kFalse); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] @@ -69,22 +69,23 @@ }; template <typename> -struct F -{ +struct F { static constexpr bool kFalse = false; F(F &&) noexcept(kFalse) = default; // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] F& operator=(F &&) noexcept(kFalse) = default; - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; struct G { G(G &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: G(G &&) noexcept = default; G& operator=(G &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: G& operator=(G &&) noexcept = default; - ThrowingMoveConstructor field; + ThrowOnAnything field; }; void throwing_function() noexcept(false) {} @@ -93,7 +94,7 @@ H(H &&) noexcept(noexcept(throwing_function())); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] H &operator=(H &&) noexcept(noexcept(throwing_function())); - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; template <typename> @@ -101,10 +102,10 @@ I(I &&) noexcept(noexcept(throwing_function())); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [performance-noexcept-move-constructor] I &operator=(I &&) noexcept(noexcept(throwing_function())); - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; -template <typename TemplateType> struct TemplatedType { +template <typename T> struct TemplatedType { static void f() {} }; @@ -119,44 +120,54 @@ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: noexcept specifier on the move assignment operator evaluates to 'false' [performance-noexcept-move-constructor] }; -struct K : public ThrowingMoveConstructor { +struct K : public ThrowOnAnything { K(K &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: K(K &&) noexcept = default; K &operator=(K &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: K &operator=(K &&) noexcept = default; }; -struct InheritFromThrowingMoveConstrcutor : public ThrowingMoveConstructor +struct InheritFromThrowOnAnything : public ThrowOnAnything {}; struct L { L(L &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: L(L &&) noexcept = default; L &operator=(L &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: L &operator=(L &&) noexcept = default; - InheritFromThrowingMoveConstrcutor IFF; + InheritFromThrowOnAnything IFF; }; -struct M : public InheritFromThrowingMoveConstrcutor { +struct M : public InheritFromThrowOnAnything { M(M &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: M(M &&) noexcept = default; M &operator=(M &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: M &operator=(M &&) noexcept = default; }; -struct N : public IntWrapper, ThrowingMoveConstructor { +struct N : public IntWrapper, ThrowOnAnything { N(N &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: N(N &&) noexcept = default; N &operator=(N &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: N &operator=(N &&) noexcept = default; }; -struct O : virtual IntWrapper, ThrowingMoveConstructor { +struct O : virtual IntWrapper, ThrowOnAnything { O(O &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: O(O &&) noexcept = default; O &operator=(O &&) = default; // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should be marked noexcept [performance-noexcept-move-constructor] + // CHECK-FIXES: O &operator=(O &&) noexcept = default; }; class OK {}; @@ -166,9 +177,7 @@ a = OK(); } -class OK1 { -public: - OK1(); +struct OK1 { OK1(const OK1 &); OK1(OK1 &&) noexcept; OK1 &operator=(OK1 &&) noexcept; @@ -183,14 +192,14 @@ OK2 &operator=(OK2 &&) noexcept(kTrue) { return *this; } }; -struct OK3 { - OK3(OK3 &&) noexcept(false) {} - OK3 &operator=(OK3 &&) = delete; +struct OK4 { + OK4(OK4 &&) noexcept(false) {} + OK4 &operator=(OK4 &&) = delete; }; -struct OK4 { - OK4(OK4 &&) noexcept = default; - OK4 &operator=(OK4 &&) noexcept = default; +struct OK3 { + OK3(OK3 &&) noexcept = default; + OK3 &operator=(OK3 &&) noexcept = default; }; struct OK5 { @@ -245,7 +254,8 @@ OK13 &operator=(OK13 &&) noexcept(noexcept(noexcept_function)) = default; }; -template <typename> struct OK14 { +template <typename> +struct OK14 { OK14(OK14 &&) noexcept(noexcept(TemplatedType<int>::f())); OK14 &operator=(OK14 &&) noexcept(noexcept(TemplatedType<int>::f())); }; @@ -265,45 +275,40 @@ int member; }; -struct OK17 -{ +struct OK17 { OK17(OK17 &&) = default; OK17 &operator=(OK17 &&) = default; + OK empty_field; }; template <typename> -struct OK18 -{ +struct OK18 { OK18(OK18 &&) = default; OK18 &operator=(OK18 &&) = default; OK empty_field; }; -struct OK19 : public OK -{ +struct OK19 : public OK { OK19(OK19 &&) = default; OK19 &operator=(OK19 &&) = default; }; -struct OK20 : virtual OK -{ +struct OK20 : virtual OK { OK20(OK20 &&) = default; OK20 &operator=(OK20 &&) = default; }; template <typename T> -struct OK21 : public T -{ +struct OK21 : public T { OK21() = default; OK21(OK21 &&) = default; OK21 &operator=(OK21 &&) = default; }; template <typename T> -struct OK22 : virtual T -{ +struct OK22 : virtual T { OK22() = default; OK22(OK22 &&) = default; OK22 &operator=(OK22 &&) = default; @@ -311,7 +316,7 @@ template <typename T> struct OK23 { - OK23()= default; + OK23() = default; OK23(OK23 &&) = default; OK23 &operator=(OK23 &&) = default; @@ -345,15 +350,13 @@ }; template <typename T> -struct OK27 : public T -{ +struct OK27 : public T { OK27(OK27 &&) = default; OK27 &operator=(OK27 &&) = default; }; template <typename T> -struct OK28 : virtual T -{ +struct OK28 : virtual T { OK28(OK28 &&) = default; OK28 &operator=(OK28 &&) = default; }; Index: clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-destructor.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-destructor.cpp @@ -0,0 +1,306 @@ +// RUN: %check_clang_tidy %s performance-noexcept-destructor %t -- -- -fexceptions + +struct Empty +{}; + +struct IntWrapper { + int value; +}; + +template <typename T> +struct FalseT { + static constexpr bool value = false; +}; + +template <typename T> +struct TrueT { + static constexpr bool value = true; +}; + +struct ThrowOnAnything { + ThrowOnAnything() noexcept(false); + ThrowOnAnything(ThrowOnAnything&&) noexcept(false); + ThrowOnAnything& operator=(ThrowOnAnything &&) noexcept(false); + ~ThrowOnAnything() noexcept(false); +}; + +struct B { + static constexpr bool kFalse = false; + ~B() noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +struct D { + static constexpr bool kFalse = false; + ~D() noexcept(kFalse) = default; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +template <typename> +struct E { + static constexpr bool kFalse = false; + ~E() noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' +}; + +template <typename> +struct F { + static constexpr bool kFalse = false; + ~F() noexcept(kFalse) = default; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +struct G { + ~G() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~G() noexcept = default; + + ThrowOnAnything field; +}; + +void throwing_function() noexcept(false) {} + +struct H { + ~H() noexcept(noexcept(throwing_function())); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +template <typename> +struct I { + ~I() noexcept(noexcept(throwing_function())); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +template <typename T> struct TemplatedType { + static void f() {} +}; + +template <> struct TemplatedType<int> { + static void f() noexcept {} +}; + +struct J { + ~J() noexcept(noexcept(TemplatedType<double>::f())); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: noexcept specifier on the destructor evaluates to 'false' [performance-noexcept-destructor] +}; + +struct K : public ThrowOnAnything { + ~K() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~K() noexcept = default; +}; + +struct InheritFromThrowOnAnything : public ThrowOnAnything +{}; + +struct L { + ~L() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~L() noexcept = default; + + InheritFromThrowOnAnything IFF; +}; + +struct M : public InheritFromThrowOnAnything { + ~M() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~M() noexcept = default; +}; + +struct N : public IntWrapper, ThrowOnAnything { + ~N() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~N() noexcept = default; +}; + +struct O : virtual IntWrapper, ThrowOnAnything { + ~O() = default; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: destructors should be marked noexcept [performance-noexcept-destructor] + // CHECK-FIXES: ~O() noexcept = default; +}; + +class OK {}; + +struct OK1 { + ~OK1() noexcept; +}; + +struct OK2 { + static constexpr bool kTrue = true; + + ~OK2() noexcept(true) {} +}; + +struct OK4 { + ~OK4() noexcept(false) {} +}; + +struct OK3 { + ~OK3() = default; +}; + +struct OK5 { + ~OK5() noexcept(true) = default; +}; + +struct OK6 { + ~OK6() = default; +}; + +template <typename> +struct OK7 { + ~OK7() = default; +}; + +template <typename> +struct OK8 { + ~OK8() noexcept = default; +}; + +template <typename> +struct OK9 { + ~OK9() noexcept(true) = default; +}; + +template <typename> +struct OK10 { + ~OK10() noexcept(false) = default; +}; + +template <typename> +struct OK11 { + ~OK11() = delete; +}; + +void noexcept_function() noexcept {} + +struct OK12 { + ~OK12() noexcept(noexcept(noexcept_function())); +}; + +struct OK13 { + ~OK13() noexcept(noexcept(noexcept_function())) = default; +}; + +template <typename> +struct OK14 { + ~OK14() noexcept(noexcept(TemplatedType<int>::f())); +}; + +struct OK15 { + ~OK15() = default; + + int member; +}; + +template <typename> +struct OK16 { + ~OK16() = default; + + int member; +}; + +struct OK17 { + ~OK17() = default; + + OK empty_field; +}; + +template <typename> +struct OK18 { + ~OK18() = default; + + OK empty_field; +}; + +struct OK19 : public OK { + ~OK19() = default; +}; + +struct OK20 : virtual OK { + ~OK20() = default; +}; + +template <typename T> +struct OK21 : public T { + ~OK21() = default; +}; + +template <typename T> +struct OK22 : virtual T { + ~OK22() = default; +}; + +template <typename T> +struct OK23 { + ~OK23() = default; + + T member; +}; + +void testTemplates() { + OK21<Empty> value(OK21<Empty>{}); + value = OK21<Empty>{}; + + OK22<Empty> value2{OK22<Empty>{}}; + value2 = OK22<Empty>{}; + + OK23<Empty> value3{OK23<Empty>{}}; + value3 =OK23<Empty>{}; +} + +struct OK24 : public Empty, OK1 { + ~OK24() = default; +}; + +struct OK25 : virtual Empty, OK1 { + ~OK25() = default; +}; + +struct OK26 : public Empty, IntWrapper { + ~OK26() = default; +}; + +template <typename T> +struct OK27 : public T { + ~OK27() = default; +}; + +template <typename T> +struct OK28 : virtual T { + ~OK28() = default; +}; + +template <typename T> +struct OK29 { + ~OK29() = default; + + T member; +}; + +struct OK30 { + ~OK30() noexcept(TrueT<OK30>::value) = default; +}; + +template <typename> +struct OK31 { + ~OK31() noexcept(TrueT<int>::value) = default; +}; + +struct OK32 { + ~OK32(); +}; + +template <typename> +struct OK33 { + ~OK33(); +}; + +struct OK34 { + ~OK34() {} +}; + +template <typename> +struct OK35 { + ~OK35() {} +}; Index: clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-swap.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-swap.rst @@ -0,0 +1,13 @@ +.. title:: clang-tidy - performance-noexcept-swap + +performance-noexcept-swap +========================= + +The check flags user-defined swap functions not marked with ``noexcept`` or +marked with ``noexcept(expr)`` where ``expr`` evaluates to ``false`` +(but is not a ``false`` literal itself). + +When a swap function is marked as ``noexcept``, it assures the compiler that +no exceptions will be thrown during the swapping of two objects, which allows +the compiler to perform certain optimizations such as omitting exception +handling code. Index: clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-destructor.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/performance/noexcept-destructor.rst @@ -0,0 +1,12 @@ +.. title:: clang-tidy - performance-noexcept-destructor + +performance-noexcept-destructor +=============================== + +The check flags user-defined destructors marked with ``noexcept(expr)`` +where ``expr`` evaluates to ``false`` (but is not a ``false`` literal itself). + +When a destructor is marked as ``noexcept``, it assures the compiler that +no exceptions will be thrown during the destruction of an object, which +allows the compiler to perform certain optimizations such as omitting +exception handling code. Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -325,7 +325,9 @@ `performance-move-constructor-init <performance/move-constructor-init.html>`_, `performance-no-automatic-move <performance/no-automatic-move.html>`_, `performance-no-int-to-ptr <performance/no-int-to-ptr.html>`_, + `performance-noexcept-destructor <performance/noexcept-destructor.html>`_, "Yes" `performance-noexcept-move-constructor <performance/noexcept-move-constructor.html>`_, "Yes" + `performance-noexcept-swap <performance/noexcept-swap.html>`_, "Yes" `performance-trivially-destructible <performance/trivially-destructible.html>`_, "Yes" `performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn.html>`_, "Yes" `performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization.html>`_, "Yes" @@ -477,6 +479,9 @@ `cppcoreguidelines-c-copy-assignment-signature <cppcoreguidelines/c-copy-assignment-signature.html>`_, `misc-unconventional-assign-operator <misc/unconventional-assign-operator.html>`_, `cppcoreguidelines-explicit-virtual-functions <cppcoreguidelines/explicit-virtual-functions.html>`_, `modernize-use-override <modernize/use-override.html>`_, "Yes" `cppcoreguidelines-macro-to-enum <cppcoreguidelines/macro-to-enum.html>`_, `modernize-macro-to-enum <modernize/macro-to-enum.html>`_, "Yes" + `cppcoreguidelines-noexcept-destructor <cppcoreguidelines/noexcept-destructor.html>`_, `performance-noexcept-destructor <performance/noexcept-destructor.html>`_, "Yes" + `cppcoreguidelines-noexcept-move-operations <cppcoreguidelines/noexcept-move-operations.html>`_, `performance-noexcept-move-constructor <performance/noexcept-move-constructor.html>`_, "Yes" + `cppcoreguidelines-noexcept-swap <cppcoreguidelines/noexcept-swap.html>`_, `performance-noexcept-swap <performance/noexcept-swap.html>`_, "Yes" `cppcoreguidelines-non-private-member-variables-in-classes <cppcoreguidelines/non-private-member-variables-in-classes.html>`_, `misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes.html>`_, `cppcoreguidelines-use-default-member-init <cppcoreguidelines/use-default-member-init.html>`_, `modernize-use-default-member-init <modernize/use-default-member-init.html>`_, `fuchsia-header-anon-namespaces <fuchsia/header-anon-namespaces.html>`_, `google-build-namespaces <google/build-namespaces.html>`_, Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-swap.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-swap.rst @@ -0,0 +1,15 @@ +.. title:: clang-tidy - cppcoreguidelines-noexcept-swap +.. meta:: + :http-equiv=refresh: 5;URL=../performance/noexcept-swap.html + +cppcoreguidelines-noexcept-swap +=============================== + +This check implements `C.83 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c83-for-value-like-types-consider-providing-a-noexcept-swap-function>`_ +, `C.84 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c84-a-swap-function-must-not-fail>`_ +and `C.85 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c85-make-swap-noexcept>`_ +from the CppCoreGuidelines. + +The cppcoreguidelines-noexcept-swap check is an alias, please see +`performance-noexcept-swap <../performance/noexcept-swap.html>`_ +for more information. Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-move-operations.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-move-operations.rst @@ -0,0 +1,13 @@ +.. title:: clang-tidy - cppcoreguidelines-noexcept-move-operations +.. meta:: + :http-equiv=refresh: 5;URL=../performance/noexcept-move-constructor.html + +cppcoreguidelines-noexcept-move-operations +========================================== + +This check implements `C.66 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c66-make-move-operations-noexcept>`_ +from the CppCoreGuidelines. + +The cppcoreguidelines-noexcept-move-operations check is an alias, please see +`performance-noexcept-move-constructor <../performance/noexcept-move-constructor.html>`_ +for more information. Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-destructor.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/noexcept-destructor.rst @@ -0,0 +1,13 @@ +.. title:: clang-tidy - cppcoreguidelines-noexcept-destructor +.. meta:: + :http-equiv=refresh: 5;URL=../performance/noexcept-destructor.html + +cppcoreguidelines-noexcept-destructor +===================================== + +This check implements `C.37 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c37-make-destructors-noexcept>`_ +from the CppCoreGuidelines. + +The cppcoreguidelines-noexcept-destructor check is an alias, please see +`performance-noexcept-destructor <../performance/noexcept-destructor.html>`_ +for more information. Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -155,6 +155,16 @@ Finds uses of ``std::endl`` on streams and replaces them with ``'\n'``. +- New :doc:`performance-noexcept-destructor + <clang-tidy/checks/performance/noexcept-destructor>` check. + + Finds user declared destructors which are not ``noexcept``. + +- New :doc:`performance-noexcept-swap + <clang-tidy/checks/performance/noexcept-swap>` check. + + Finds user declared swap functions which are not ``noexcept``. + - New :doc:`readability-avoid-unconditional-preprocessor-if <clang-tidy/checks/readability/avoid-unconditional-preprocessor-if>` check. @@ -179,6 +189,21 @@ <clang-tidy/checks/cert/msc33-c>` to :doc:`bugprone-unsafe-functions <clang-tidy/checks/bugprone/unsafe-functions>` was added. +- New alias :doc:`cppcoreguidelines-noexcept-destructor + <clang-tify/checks/cppcoreguidelines/noexcept-destructor>` to + :doc`performance-noexcept-destructor + <clang-tidy/checks/performance/noexcept-destructor>` was added. + +- New alias :doc:`cppcoreguidelines-noexcept-move-operations + <clang-tify/checks/cppcoreguidelines/noexcept-move-operations>` to + :doc`performance-noexcept-move-constructor + <clang-tidy/checks/performance/noexcept-move-constructor>` was added. + +- New alias :doc:`cppcoreguidelines-noexcept-swap + <clang-tify/checks/cppcoreguidelines/noexcept-swap>` to + :doc`performance-noexcept-swap + <clang-tidy/checks/performance/noexcept-swap>` was added. + - New alias :doc:`cppcoreguidelines-use-default-member-init <clang-tidy/checks/cppcoreguidelines/use-default-member-init>` to :doc:`modernize-use-default-member-init Index: clang-tools-extra/clang-tidy/utils/LexerUtils.h =================================================================== --- clang-tools-extra/clang-tidy/utils/LexerUtils.h +++ clang-tools-extra/clang-tidy/utils/LexerUtils.h @@ -116,6 +116,11 @@ SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM, const LangOptions &LangOpts); +/// For a given FunctionDecl returns the location where you would need to place +/// the noexcept specifier. +SourceLocation getLocationForNoexceptSpecifier(const FunctionDecl *FuncDecl, + const SourceManager &SM); + } // namespace tidy::utils::lexer } // namespace clang Index: clang-tools-extra/clang-tidy/utils/LexerUtils.cpp =================================================================== --- clang-tools-extra/clang-tidy/utils/LexerUtils.cpp +++ clang-tools-extra/clang-tidy/utils/LexerUtils.cpp @@ -20,7 +20,7 @@ Location = Location.getLocWithOffset(-1); if (Location.isInvalid()) - return Token; + return Token; auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location)); while (Location != StartOfFile) { @@ -181,7 +181,8 @@ } static bool breakAndReturnEndPlus1Token(const Stmt &S) { - return isa<Expr, DoStmt, ReturnStmt, BreakStmt, ContinueStmt, GotoStmt, SEHLeaveStmt>(S); + return isa<Expr, DoStmt, ReturnStmt, BreakStmt, ContinueStmt, GotoStmt, + SEHLeaveStmt>(S); } // Given a Stmt which does not include it's semicolon this method returns the @@ -233,11 +234,45 @@ LastChild = Child; } - if (!breakAndReturnEnd(*LastChild) && - breakAndReturnEndPlus1Token(*LastChild)) + if (!breakAndReturnEnd(*LastChild) && breakAndReturnEndPlus1Token(*LastChild)) return getSemicolonAfterStmtEndLoc(S.getEndLoc(), SM, LangOpts); return S.getEndLoc(); } +SourceLocation getLocationForNoexceptSpecifier(const FunctionDecl *FuncDecl, + const SourceManager &SM) { + if (!FuncDecl) + return {}; + + const LangOptions &LangOpts = FuncDecl->getLangOpts(); + + if (FuncDecl->getNumParams() == 0) { + // Start at the beginning of the function declaration, and find the closing + // parenthesis after which we would place the noexcept specifier. + Token CurrentToken; + SourceLocation CurrentLocation = FuncDecl->getBeginLoc(); + while (!Lexer::getRawToken(CurrentLocation, CurrentToken, SM, LangOpts, + true)) { + if (CurrentToken.is(tok::r_paren)) + return CurrentLocation.getLocWithOffset(1); + + CurrentLocation = CurrentToken.getEndLoc(); + } + + // Failed to find the closing parenthesis, so just return an invalid + // SourceLocation. + return {}; + } + + // FunctionDecl with parameters + const SourceLocation NoexceptLoc = + FuncDecl->getParamDecl(FuncDecl->getNumParams() - 1)->getEndLoc(); + if (NoexceptLoc.isValid()) + return Lexer::findLocationAfterToken(NoexceptLoc, tok::r_paren, SM, + LangOpts, true); + + return {}; +} + } // namespace clang::tidy::utils::lexer Index: clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp =================================================================== --- clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp +++ clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp @@ -134,6 +134,11 @@ if (isUnresolvedExceptionSpec(FuncProto->getExceptionSpecType())) return State::Unknown; + // A non defaulted destructor without the noexcept specifier is still noexcept + if (isa<CXXDestructorDecl>(FuncDecl) && + FuncDecl->getExceptionSpecType() == EST_None) + return State::NotThrowing; + switch (FuncProto->canThrow()) { case CT_Cannot: return State::NotThrowing; Index: clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp +++ clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp @@ -20,7 +20,9 @@ #include "MoveConstructorInitCheck.h" #include "NoAutomaticMoveCheck.h" #include "NoIntToPtrCheck.h" +#include "NoexceptDestructorCheck.h" #include "NoexceptMoveConstructorCheck.h" +#include "NoexceptSwapCheck.h" #include "TriviallyDestructibleCheck.h" #include "TypePromotionInMathFnCheck.h" #include "UnnecessaryCopyInitialization.h" @@ -52,8 +54,12 @@ CheckFactories.registerCheck<NoAutomaticMoveCheck>( "performance-no-automatic-move"); CheckFactories.registerCheck<NoIntToPtrCheck>("performance-no-int-to-ptr"); + CheckFactories.registerCheck<NoexceptDestructorCheck>( + "performance-noexcept-destructor"); CheckFactories.registerCheck<NoexceptMoveConstructorCheck>( "performance-noexcept-move-constructor"); + CheckFactories.registerCheck<NoexceptSwapCheck>( + "performance-noexcept-swap"); CheckFactories.registerCheck<TriviallyDestructibleCheck>( "performance-trivially-destructible"); CheckFactories.registerCheck<TypePromotionInMathFnCheck>( Index: clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.h =================================================================== --- clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.h +++ clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.h @@ -1,4 +1,4 @@ -//===--- NoexceptMoveConstructorCheck.h - clang-tidy-------------*- C++ -*-===// +//===--- NoexceptSwapCheck.h - clang-tidy -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,29 +6,28 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTSWAPCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTSWAPCHECK_H #include "../ClangTidyCheck.h" #include "../utils/ExceptionSpecAnalyzer.h" namespace clang::tidy::performance { -/// The check flags user-defined move constructors and assignment operators not -/// marked with `noexcept` or marked with `noexcept(expr)` where `expr` -/// evaluates to `false` (but is not a `false` literal itself). +/// The check flags swap functions not marked with `noexcept` or marked +/// with `noexcept(expr)` where `expr` evaluates to `false` +/// (but is not a `false` literal itself). /// -/// Move constructors of all the types used with STL containers, for example, -/// need to be declared `noexcept`. Otherwise STL will choose copy constructors -/// instead. The same is valid for move assignment operations. -class NoexceptMoveConstructorCheck : public ClangTidyCheck { +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/performance/noexcept-swap.html +class NoexceptSwapCheck : public ClangTidyCheck { public: - NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context) + NoexceptSwapCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus11 && LangOpts.CXXExceptions; } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: @@ -37,4 +36,4 @@ } // namespace clang::tidy::performance -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTSWAPCHECK_H Index: clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/performance/NoexceptSwapCheck.cpp @@ -0,0 +1,59 @@ +//===--- NoexceptSwapCheck.cpp - clang-tidy -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NoexceptSwapCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::performance { + +void NoexceptSwapCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + functionDecl(unless(isImplicit()), unless(isDeleted()), hasName("swap")) + .bind("decl"), + this); +} + +void NoexceptSwapCheck::check(const MatchFinder::MatchResult &Result) { + const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl"); + assert(FuncDecl); + + if (SpecAnalyzer.analyze(FuncDecl) != + utils::ExceptionSpecAnalyzer::State::Throwing) + return; + + // Don't complain about nothrow(false), but complain on nothrow(expr) + // where expr evaluates to false. + const auto *ProtoType = FuncDecl->getType()->castAs<FunctionProtoType>(); + const Expr *NoexceptExpr = ProtoType->getNoexceptExpr(); + if (NoexceptExpr) { + NoexceptExpr = NoexceptExpr->IgnoreImplicit(); + if (!isa<CXXBoolLiteralExpr>(NoexceptExpr)) { + diag(NoexceptExpr->getExprLoc(), + "noexcept specifier on swap function evaluates to 'false'"); + } + return; + } + + auto Diag = diag(FuncDecl->getLocation(), "swap functions should " + "be marked noexcept"); + + // Add FixIt hints. + const SourceManager &SM = *Result.SourceManager; + + const SourceLocation NoexceptLoc = + utils::lexer::getLocationForNoexceptSpecifier(FuncDecl, SM); + if (NoexceptLoc.isValid()) + Diag << FixItHint::CreateInsertion(NoexceptLoc, " noexcept "); +} + +} // namespace clang::tidy::performance Index: clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.cpp +++ clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "NoexceptMoveConstructorCheck.h" +#include "../utils/LexerUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" @@ -27,19 +28,18 @@ void NoexceptMoveConstructorCheck::check( const MatchFinder::MatchResult &Result) { - const auto *Decl = Result.Nodes.getNodeAs<CXXMethodDecl>("decl"); - if (!Decl) - return; + const auto *FuncDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("decl"); + assert(FuncDecl); - if (SpecAnalyzer.analyze(Decl) != + if (SpecAnalyzer.analyze(FuncDecl) != utils::ExceptionSpecAnalyzer::State::Throwing) return; - const bool IsConstructor = CXXConstructorDecl::classof(Decl); + const bool IsConstructor = CXXConstructorDecl::classof(FuncDecl); // Don't complain about nothrow(false), but complain on nothrow(expr) // where expr evaluates to false. - const auto *ProtoType = Decl->getType()->castAs<FunctionProtoType>(); + const auto *ProtoType = FuncDecl->getType()->castAs<FunctionProtoType>(); const Expr *NoexceptExpr = ProtoType->getNoexceptExpr(); if (NoexceptExpr) { NoexceptExpr = NoexceptExpr->IgnoreImplicit(); @@ -52,21 +52,18 @@ return; } - auto Diag = diag(Decl->getLocation(), + auto Diag = diag(FuncDecl->getLocation(), "move %select{assignment operator|constructor}0s should " "be marked noexcept") << IsConstructor; // Add FixIt hints. + const SourceManager &SM = *Result.SourceManager; - assert(Decl->getNumParams() > 0); - SourceLocation NoexceptLoc = - Decl->getParamDecl(Decl->getNumParams() - 1)->getSourceRange().getEnd(); - if (NoexceptLoc.isValid()) - NoexceptLoc = Lexer::findLocationAfterToken( - NoexceptLoc, tok::r_paren, SM, Result.Context->getLangOpts(), true); + + const SourceLocation NoexceptLoc = + utils::lexer::getLocationForNoexceptSpecifier(FuncDecl, SM); if (NoexceptLoc.isValid()) Diag << FixItHint::CreateInsertion(NoexceptLoc, " noexcept "); - return; } } // namespace clang::tidy::performance Index: clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.h =================================================================== --- clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.h +++ clang-tools-extra/clang-tidy/performance/NoexceptMoveConstructorCheck.h @@ -21,6 +21,9 @@ /// Move constructors of all the types used with STL containers, for example, /// need to be declared `noexcept`. Otherwise STL will choose copy constructors /// instead. The same is valid for move assignment operations. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/performance/noexcept-move-constructor.html class NoexceptMoveConstructorCheck : public ClangTidyCheck { public: NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context) Index: clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.h =================================================================== --- clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.h +++ clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.h @@ -1,4 +1,4 @@ -//===--- NoexceptMoveConstructorCheck.h - clang-tidy-------------*- C++ -*-===// +//===--- NoexceptDestructorCheck.h - clang-tidy -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,29 +6,28 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTDESTRUCTORCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTDESTRUCTORCHECK_H #include "../ClangTidyCheck.h" #include "../utils/ExceptionSpecAnalyzer.h" namespace clang::tidy::performance { -/// The check flags user-defined move constructors and assignment operators not -/// marked with `noexcept` or marked with `noexcept(expr)` where `expr` -/// evaluates to `false` (but is not a `false` literal itself). +/// The check flags destructors not marked with `noexcept` or marked +/// with `noexcept(expr)` where `expr` evaluates to `false` +/// (but is not a `false` literal itself). /// -/// Move constructors of all the types used with STL containers, for example, -/// need to be declared `noexcept`. Otherwise STL will choose copy constructors -/// instead. The same is valid for move assignment operations. -class NoexceptMoveConstructorCheck : public ClangTidyCheck { +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/performance/noexcept-destructor.html +class NoexceptDestructorCheck : public ClangTidyCheck { public: - NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context) + NoexceptDestructorCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus11 && LangOpts.CXXExceptions; } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: @@ -37,4 +36,4 @@ } // namespace clang::tidy::performance -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTDESTRUCTORCHECK_H Index: clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/performance/NoexceptDestructorCheck.cpp @@ -0,0 +1,58 @@ +//===--- NoexceptDestructorCheck.cpp - clang-tidy -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NoexceptDestructorCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::performance { + +void NoexceptDestructorCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(functionDecl(unless(isImplicit()), unless(isDeleted()), + cxxDestructorDecl()) + .bind("decl"), + this); +} + +void NoexceptDestructorCheck::check(const MatchFinder::MatchResult &Result) { + const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl"); + assert(FuncDecl); + + if (SpecAnalyzer.analyze(FuncDecl) != + utils::ExceptionSpecAnalyzer::State::Throwing) + return; + + // Don't complain about nothrow(false), but complain on nothrow(expr) + // where expr evaluates to false. + const auto *ProtoType = FuncDecl->getType()->castAs<FunctionProtoType>(); + const Expr *NoexceptExpr = ProtoType->getNoexceptExpr(); + if (NoexceptExpr) { + NoexceptExpr = NoexceptExpr->IgnoreImplicit(); + if (!isa<CXXBoolLiteralExpr>(NoexceptExpr)) { + diag(NoexceptExpr->getExprLoc(), + "noexcept specifier on the destructor evaluates to 'false'"); + } + return; + } + + auto Diag = diag(FuncDecl->getLocation(), "destructors should " + "be marked noexcept"); + + // Add FixIt hints. + const SourceManager &SM = *Result.SourceManager; + + const SourceLocation NoexceptLoc = + utils::lexer::getLocationForNoexceptSpecifier(FuncDecl, SM); + if (NoexceptLoc.isValid()) + Diag << FixItHint::CreateInsertion(NoexceptLoc, " noexcept "); +} + +} // namespace clang::tidy::performance Index: clang-tools-extra/clang-tidy/performance/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/performance/CMakeLists.txt +++ clang-tools-extra/clang-tidy/performance/CMakeLists.txt @@ -15,7 +15,9 @@ MoveConstructorInitCheck.cpp NoAutomaticMoveCheck.cpp NoIntToPtrCheck.cpp + NoexceptDestructorCheck.cpp NoexceptMoveConstructorCheck.cpp + NoexceptSwapCheck.cpp PerformanceTidyModule.cpp TriviallyDestructibleCheck.cpp TypePromotionInMathFnCheck.cpp Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -14,6 +14,9 @@ #include "../modernize/AvoidCArraysCheck.h" #include "../modernize/UseDefaultMemberInitCheck.h" #include "../modernize/UseOverrideCheck.h" +#include "../performance/NoexceptDestructorCheck.h" +#include "../performance/NoexceptMoveConstructorCheck.h" +#include "../performance/NoexceptSwapCheck.h" #include "../readability/MagicNumbersCheck.h" #include "AvoidCapturingLambdaCoroutinesCheck.h" #include "AvoidConstOrRefDataMembersCheck.h" @@ -80,6 +83,12 @@ CheckFactories.registerCheck<NarrowingConversionsCheck>( "cppcoreguidelines-narrowing-conversions"); CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc"); + CheckFactories.registerCheck<performance::NoexceptDestructorCheck>( + "cppcoreguidelines-noexcept-destructor"); + CheckFactories.registerCheck<performance::NoexceptMoveConstructorCheck>( + "cppcoreguidelines-noexcept-move-operations"); + CheckFactories.registerCheck<performance::NoexceptSwapCheck>( + "cppcoreguidelines-noexcept-swap"); CheckFactories.registerCheck<misc::NonPrivateMemberVariablesInClassesCheck>( "cppcoreguidelines-non-private-member-variables-in-classes"); CheckFactories.registerCheck<OwningMemoryCheck>(
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits