Index: clang-tools-extra/trunk/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp @@ -22,26 +22,44 @@ return; Finder->addMatcher( - cxxThrowExpr(allOf(has(expr(unless(hasType(qualType(hasCanonicalType( - hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom( - hasName("std::exception")))))))))), - has(expr(unless(cxxUnresolvedConstructExpr()))), - eachOf(has(expr(hasType(namedDecl().bind("decl")))), - anything()))) + cxxThrowExpr( + allOf( + unless(has(expr(anyOf(isTypeDependent(), isValueDependent())))), + // The thrown value is not derived from 'std::exception'. + has(expr(unless(hasType( + qualType(hasCanonicalType(hasDeclaration(cxxRecordDecl( + isSameOrDerivedFrom(hasName("::std::exception")))))))))), + // This condition is always true, but will bind to the + // template value if the thrown type is templated. + anyOf(has(expr(hasType( + substTemplateTypeParmType().bind("templ_type")))), + anything()), + // Bind to the declaration of the type of the value that + // is thrown. 'anything()' is necessary to always suceed + // in the 'eachOf' because builtin types are not + // 'namedDecl'. + eachOf(has(expr(hasType(namedDecl().bind("decl")))), anything()))) .bind("bad_throw"), this); } void ExceptionBaseclassCheck::check(const MatchFinder::MatchResult &Result) { const auto *BadThrow = Result.Nodes.getNodeAs("bad_throw"); + assert(BadThrow && "Did not match the throw expression"); diag(BadThrow->getSubExpr()->getBeginLoc(), "throwing an exception whose " "type %0 is not derived from " "'std::exception'") << BadThrow->getSubExpr()->getType() << BadThrow->getSourceRange(); - const auto *TypeDecl = Result.Nodes.getNodeAs("decl"); - if (TypeDecl != nullptr) + if (const auto *Template = + Result.Nodes.getNodeAs("templ_type")) + diag(BadThrow->getSubExpr()->getBeginLoc(), + "type %0 is a template instantiation of %1", DiagnosticIDs::Note) + << BadThrow->getSubExpr()->getType() + << Template->getReplacedParameter()->getDecl(); + + if (const auto *TypeDecl = Result.Nodes.getNodeAs("decl")) diag(TypeDecl->getBeginLoc(), "type defined here", DiagnosticIDs::Note); } Index: clang-tools-extra/trunk/test/clang-tidy/hicpp-exception-baseclass.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/hicpp-exception-baseclass.cpp +++ clang-tools-extra/trunk/test/clang-tidy/hicpp-exception-baseclass.cpp @@ -2,6 +2,7 @@ namespace std { class exception {}; +class invalid_argument : public exception {}; } // namespace std class derived_exception : public std::exception {}; @@ -36,12 +37,12 @@ try { throw non_derived_exception(); // CHECK-NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-NOTES: 9:1: note: type defined here + // CHECK-NOTES: 10:1: note: type defined here } catch (non_derived_exception &e) { } throw non_derived_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-NOTES: 9:1: note: type defined here + // CHECK-NOTES: 10:1: note: type defined here // FIXME: More complicated kinds of inheritance should be checked later, but there is // currently no way use ASTMatchers for this kind of task. @@ -49,25 +50,25 @@ // Handle private inheritance cases correctly. try { throw bad_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 10:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 11:1: note: type defined here throw no_good_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 11:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 12:1: note: type defined here throw really_creative(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' - // CHECK MESSAGES: 12:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' + // CHECK NOTES: 13:1: note: type defined here } catch (...) { } throw bad_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 10:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 11:1: note: type defined here throw no_good_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 11:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 12:1: note: type defined here throw really_creative(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' - // CHECK MESSAGES: 12:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' + // CHECK NOTES: 13:1: note: type defined here #endif } @@ -91,24 +92,40 @@ throw deep_hierarchy(); // Ok try { - throw terrible_idea(); // Ok, but multiple inheritance isn't clean + throw terrible_idea(); // Ok, but multiple inheritance isn't clean } catch (std::exception &e) { // Can be caught as std::exception, even with multiple inheritance } throw terrible_idea(); // Ok, but multiple inheritance } +void test_lambdas() { + auto BadLambda = []() { throw int(42); }; + // CHECK-NOTES: [[@LINE-1]]:33: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + auto GoodLambda = []() { throw derived_exception(); }; +} + // Templated function that throws exception based on template type template void ThrowException() { throw T(); } // CHECK-NOTES: [[@LINE-1]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 120:1: note: type defined here -// CHECK-NOTES: [[@LINE-3]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 120:1: note: type defined here -// CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 123:1: note: type defined here -// CHECK-NOTES: [[@LINE-7]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' -// CHECK-NOTES: [[@LINE-8]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' -// CHECK-NOTES: 9:1: note: type defined here +// CHECK-NOTES: [[@LINE-2]]:31: note: type 'bad_generic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+25]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-6]]:31: note: type 'bad_generic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+21]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-9]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-10]]:31: note: type 'exotic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+20]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-13]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-14]]:31: note: type 'int' is a template instantiation of 'T' + +// CHECK-NOTES: [[@LINE-16]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-17]]:31: note: type 'non_derived_exception' is a template instantiation of 'T' +// CHECK-NOTES: 10:1: note: type defined here + #define THROW_EXCEPTION(CLASS) ThrowException() #define THROW_BAD_EXCEPTION throw int(42); #define THROW_GOOD_EXCEPTION throw std::exception(); @@ -125,17 +142,14 @@ void generic_exceptions() { THROW_EXCEPTION(int); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' THROW_EXCEPTION(non_derived_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK MESSAGES: 9:1: note: type defined here THROW_EXCEPTION(std::exception); // Ok THROW_EXCEPTION(derived_exception); // Ok THROW_EXCEPTION(deep_hierarchy); // Ok THROW_BAD_EXCEPTION; // CHECK-NOTES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' - // CHECK-NOTES: [[@LINE-25]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' + // CHECK-NOTES: [[@LINE-22]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' THROW_GOOD_EXCEPTION; THROW_DERIVED_EXCEPTION; @@ -144,20 +158,17 @@ throw bad_generic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 120:1: note: type defined here + // CHECK-NOTES: [[@LINE-24]]:1: note: type defined here throw bad_generic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 120:1: note: type defined here + // CHECK-NOTES: [[@LINE-27]]:1: note: type defined here THROW_EXCEPTION(bad_generic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' THROW_EXCEPTION(bad_generic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' throw exotic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 123:1: note: type defined here + // CHECK-NOTES: [[@LINE-30]]:1: note: type defined here THROW_EXCEPTION(exotic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' throw exotic_exception(); // Ok THROW_EXCEPTION(exotic_exception); // Ok @@ -172,11 +183,102 @@ void typedefed() { throw TypedefedBad(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'TypedefedBad' (aka 'int') is not derived from 'std::exception' - // CHECK-NOTES: 167:1: note: type defined here + // CHECK-NOTES: [[@LINE-8]]:1: note: type defined here throw TypedefedGood(); // Ok throw UsingBad(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'UsingBad' (aka 'int') is not derived from 'std::exception' - // CHECK-NOTES: 169:1: note: type defined here + // CHECK-NOTES: [[@LINE-11]]:1: note: type defined here throw UsingGood(); // Ok } + +// Fix PR37913 +struct invalid_argument_maker { + ::std::invalid_argument operator()() const; +}; +struct int_maker { + int operator()() const; +}; + +template +void templated_thrower() { + throw T{}(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} +template +void templated_thrower2() { + T ExceptionFactory; // This test found a which did not happend with 'throw T{}()' + throw ExceptionFactory(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +void exception_created_with_function() { + templated_thrower(); + templated_thrower(); + + templated_thrower2(); + templated_thrower2(); + + throw invalid_argument_maker{}(); + throw int_maker{}(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +struct invalid_argument_factory { + ::std::invalid_argument make_exception() const; +}; + +struct int_factory { + int make_exception() const; +}; + +template +void templated_factory() { + T f; + throw f.make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} +template +void templated_factory2() { + throw T().make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +void exception_from_factory() { + templated_factory(); + templated_factory(); + + templated_factory2(); + templated_factory2(); + + throw invalid_argument_factory().make_exception(); + throw int_factory().make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + + invalid_argument_factory inv_f; + throw inv_f.make_exception(); + + int_factory int_f; + throw int_f.make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +template +struct ThrowClassTemplateParam { + ThrowClassTemplateParam() { throw T(); } + // CHECK-NOTES: [[@LINE-1]]:37: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-2]]:37: note: type 'int' is a template instantiation of 'T' +}; + +template +struct ThrowValueTemplate { + ThrowValueTemplate() { throw V; } + // CHECK-NOTES: [[@LINE-1]]:32: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +}; + +void class_templates() { + ThrowClassTemplateParam IntThrow; + ThrowClassTemplateParam ArgThrow; + + ThrowValueTemplate<42> ValueThrow; +}