diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ExceptionAnalyzer.h" +#include namespace clang::tidy::utils { @@ -149,37 +150,27 @@ } bool isFunctionPointerConvertible(QualType From, QualType To) { - if (!From->isFunctionPointerType() && !From->isFunctionType() && - !From->isMemberFunctionPointerType()) - return false; - - if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType()) + ODRHash FromHash, ToHash; + + if (From->isFunctionPointerType()) + FromHash.AddType(From->getPointeeType().getTypePtr()); + else if (From->isFunctionType()) + FromHash.AddType(From->castAs()); + else if (From->isMemberFunctionPointerType()) + FromHash.AddType(From->castAs()); + else return false; - if (To->isFunctionPointerType()) { - if (From->isFunctionPointerType()) - return To->getPointeeType() == From->getPointeeType(); - - if (From->isFunctionType()) - return To->getPointeeType() == From; - + if (To->isFunctionPointerType()) + ToHash.AddType(To->getPointeeType().getTypePtr()); + else if (From->isFunctionType()) + ToHash.AddType(To->castAs()); + else if (From->isMemberFunctionPointerType()) + ToHash.AddType(To->castAs()); + else return false; - } - - if (To->isMemberFunctionPointerType()) { - if (!From->isMemberFunctionPointerType()) - return false; - - const auto *FromMember = cast(From); - const auto *ToMember = cast(To); - // Note: converting Derived::* to Base::* is a different kind of conversion, - // called Pointer-to-member conversion. - return FromMember->getClass() == ToMember->getClass() && - FromMember->getPointeeType() == ToMember->getPointeeType(); - } - - return false; + return FromHash.CalculateHash() == ToHash.CalculateHash(); } // Checks if From is qualification convertible to To based on the current @@ -278,16 +269,17 @@ return false; if (!isSameP_i(From, To)) { - if (LangOpts.CPlusPlus20) { - if (From->isConstantArrayType() && !To->isIncompleteArrayType()) - return false; + if (!LangOpts.CPlusPlus20) + return false; - if (From->isIncompleteArrayType() && !To->isIncompleteArrayType()) - return false; + if (From->isConstantArrayType() && !To->isIncompleteArrayType()) + return false; - } else { + if (From->isIncompleteArrayType() && !To->isIncompleteArrayType()) + return false; + + if (From->isMemberPointerType() || To->isMemberPointerType()) return false; - } } ++I; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp @@ -29,3 +29,28 @@ sub_throws() throw() : super_throws() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws' which should not throw exceptions }; + +namespace PR55143 { namespace PR40583 { + +struct test_explicit_throw { + test_explicit_throw() throw(int) { throw 42; } + test_explicit_throw(const test_explicit_throw&) throw(int) { throw 42; } + test_explicit_throw(test_explicit_throw&&) throw(int) { throw 42; } + test_explicit_throw& operator=(const test_explicit_throw&) throw(int) { throw 42; } + test_explicit_throw& operator=(test_explicit_throw&&) throw(int) { throw 42; } + ~test_explicit_throw() throw(int) { throw 42; } +}; + +struct test_implicit_throw { + test_implicit_throw() { throw 42; } + test_implicit_throw(const test_implicit_throw&) { throw 42; } + test_implicit_throw(test_implicit_throw&&) { throw 42; } + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'test_implicit_throw' which should not throw exceptions + test_implicit_throw& operator=(const test_implicit_throw&) { throw 42; } + test_implicit_throw& operator=(test_implicit_throw&&) { throw 42; } + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: an exception may be thrown in function 'operator=' which should not throw exceptions + ~test_implicit_throw() { throw 42; } + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function '~test_implicit_throw' which should not throw exceptions +}; + +}} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp @@ -1,10 +1,9 @@ -// RUN: %check_clang_tidy -std=c++11,c++14 %s bugprone-exception-escape %t -- \ +// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-exception-escape %t -- \ // RUN: -config="{CheckOptions: { \ // RUN: bugprone-exception-escape.IgnoredExceptions: 'ignored1,ignored2', \ // RUN: bugprone-exception-escape.FunctionsThatShouldNotThrow: 'enabled1,enabled2,enabled3' \ // RUN: }}" \ // RUN: -- -fexceptions -// FIXME: Fix the checker to work in C++17 or later mode. struct throwing_destructor { ~throwing_destructor() { @@ -420,6 +419,7 @@ struct baseMember { int *iptr; virtual void foo(){}; + void boo(){}; }; struct derivedMember : baseMember { @@ -441,6 +441,29 @@ } } +void throw_basefn_catch_const_basefn() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basefn_catch_const_basefn' which should not throw exceptions + try { + throw &baseMember::foo; + } catch(const void(baseMember::*)()) { + } +} + +void throw_derivedfn_catch_basefn() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derivedfn_catch_basefn' which should not throw exceptions + try { + throw &derivedMember::foo; + } catch(void(baseMember::*)()) { + } +} + +void throw_basefn_via_derivedfn_catch_basefn() noexcept { + try { + throw &derivedMember::boo; + } catch(void(baseMember::*)()) { + } +} + void throw_basem_catch_basem_throw() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basem_catch_basem_throw' which should not throw exceptions try { @@ -721,28 +744,3 @@ test_basic_no_throw(); test_basic_throw(); } - -namespace PR55143 { namespace PR40583 { - -struct test_explicit_throw { - test_explicit_throw() throw(int) { throw 42; } - test_explicit_throw(const test_explicit_throw&) throw(int) { throw 42; } - test_explicit_throw(test_explicit_throw&&) throw(int) { throw 42; } - test_explicit_throw& operator=(const test_explicit_throw&) throw(int) { throw 42; } - test_explicit_throw& operator=(test_explicit_throw&&) throw(int) { throw 42; } - ~test_explicit_throw() throw(int) { throw 42; } -}; - -struct test_implicit_throw { - test_implicit_throw() { throw 42; } - test_implicit_throw(const test_implicit_throw&) { throw 42; } - test_implicit_throw(test_implicit_throw&&) { throw 42; } - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'test_implicit_throw' which should not throw exceptions - test_implicit_throw& operator=(const test_implicit_throw&) { throw 42; } - test_implicit_throw& operator=(test_implicit_throw&&) { throw 42; } - // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: an exception may be thrown in function 'operator=' which should not throw exceptions - ~test_implicit_throw() { throw 42; } - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function '~test_implicit_throw' which should not throw exceptions -}; - -}}