diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -68,13 +68,22 @@ if (!MatchedDecl) return; - if (Tracer.analyze(MatchedDecl).getBehaviour() == - utils::ExceptionAnalyzer::State::Throwing) - // FIXME: We should provide more information about the exact location where - // the exception is thrown, maybe the full path the exception escapes - diag(MatchedDecl->getLocation(), "an exception may be thrown in function " - "%0 which should not throw exceptions") - << MatchedDecl; + const utils::ExceptionAnalyzer::ExceptionInfo AnalyzeResult = + Tracer.analyze(MatchedDecl); + if (AnalyzeResult.getBehaviour() != utils::ExceptionAnalyzer::State::Throwing) + return; + + diag(MatchedDecl->getLocation(), "an exception may be thrown in function " + "%0 which should not throw exceptions") + << MatchedDecl; + + for (auto [ThrowType, ThrowLoc] : AnalyzeResult.getExceptionTypes()) + diag(ThrowLoc, "may throw %0 here", DiagnosticIDs::Note) + << QualType(ThrowType, 0U); + + if (AnalyzeResult.containsUnknownElements()) + diag(MatchedDecl->getLocation(), "may throw unknown exceptions", + DiagnosticIDs::Note); } } // namespace tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h @@ -13,6 +13,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSet.h" +#include namespace clang::tidy::utils { @@ -35,9 +36,23 @@ /// does not include *ALL* possible types as there is the possibility that /// an 'Unknown' function is called that might throw a previously unknown /// exception at runtime. + class ExceptionInfo { public: - using Throwables = llvm::SmallSet; + struct ThrowInfo { + const Type *ThrowType; + SourceLocation ThrowLoc; + + friend bool operator<(const ThrowInfo &L, const ThrowInfo &R) noexcept { + return L.ThrowType < R.ThrowType; + } + + friend bool operator==(const ThrowInfo &L, const ThrowInfo &R) noexcept { + return L.ThrowType == R.ThrowType; + } + }; + + using Throwables = llvm::SmallSet; static ExceptionInfo createUnknown() { return ExceptionInfo(State::Unknown); } @@ -60,11 +75,12 @@ /// Register a single exception type as recognized potential exception to be /// thrown. - void registerException(const Type *ExceptionType); + void registerException(const Type *ExceptionType, const SourceLocation Loc); /// Registers a `SmallVector` of exception types as recognized potential /// exceptions to be thrown. - void registerExceptions(const Throwables &Exceptions); + void registerExceptions(const Throwables &Exceptions, + const SourceLocation Loc); /// Updates the local state according to the other state. That means if /// for example a function contains multiple statements the 'ExceptionInfo' @@ -78,7 +94,8 @@ /// possible to catch multiple exception types by one 'catch' if they /// are a subclass of the 'catch'ed exception type. /// Returns 'true' if some exceptions were filtered, otherwise 'false'. - bool filterByCatch(const Type *BaseClass, const ASTContext &Context); + bool filterByCatch(const Type *BaseClass, const ASTContext &Context, + Throwables &CaughtExceptions); /// Filter the set of thrown exception type against a set of ignored /// types that shall not be considered in the exception analysis. 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 @@ -11,18 +11,20 @@ namespace clang::tidy::utils { void ExceptionAnalyzer::ExceptionInfo::registerException( - const Type *ExceptionType) { + const Type *ExceptionType, const SourceLocation Loc) { assert(ExceptionType != nullptr && "Only valid types are accepted"); Behaviour = State::Throwing; - ThrownExceptions.insert(ExceptionType); + ThrownExceptions.insert({ExceptionType, Loc}); } void ExceptionAnalyzer::ExceptionInfo::registerExceptions( - const Throwables &Exceptions) { - if (Exceptions.size() == 0) + const Throwables &Exceptions, const SourceLocation Loc) { + if (Exceptions.empty()) return; Behaviour = State::Throwing; - ThrownExceptions.insert(Exceptions.begin(), Exceptions.end()); + for (const ThrowInfo &Info : Exceptions) + ThrownExceptions.insert( + {Info.ThrowType, Info.ThrowLoc.isInvalid() ? Loc : Info.ThrowLoc}); } ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge( @@ -317,9 +319,10 @@ } // namespace bool ExceptionAnalyzer::ExceptionInfo::filterByCatch( - const Type *HandlerTy, const ASTContext &Context) { + const Type *HandlerTy, const ASTContext &Context, + ExceptionInfo::Throwables &CaughtExceptions) { llvm::SmallVector TypesToDelete; - for (const Type *ExceptionTy : ThrownExceptions) { + for (auto [ExceptionTy, ThrowLoc] : ThrownExceptions) { CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified(); CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified(); @@ -327,13 +330,17 @@ // (ignoring the top-level cv-qualifiers) ... if (ExceptionCanTy == HandlerCanTy) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); + continue; } // The handler is of type cv T or cv T& and T is an unambiguous public base // class of E ... - else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(), - HandlerCanTy->getTypePtr())) { + if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(), + HandlerCanTy->getTypePtr())) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); + continue; } if (HandlerCanTy->getTypeClass() == Type::RValueReference || @@ -352,15 +359,21 @@ ExceptionCanTy->getTypePtr()->getPointeeType().getTypePtr(), HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr())) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); + continue; } // A function pointer conversion ... - else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) { + if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); + continue; } // A a qualification conversion ... - else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy, - Context.getLangOpts())) { + if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy, + Context.getLangOpts())) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); + continue; } } @@ -369,11 +382,12 @@ else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) && ExceptionCanTy->isNullPtrType()) { TypesToDelete.push_back(ExceptionTy); + CaughtExceptions.insert({ExceptionTy, ThrowLoc}); } } for (const Type *T : TypesToDelete) - ThrownExceptions.erase(T); + ThrownExceptions.erase({T, SourceLocation()}); reevaluateBehaviour(); return TypesToDelete.size() > 0; @@ -385,7 +399,7 @@ llvm::SmallVector TypesToDelete; // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible. // Therefore this slightly hacky implementation is required. - for (const Type *T : ThrownExceptions) { + for (auto [T, TLoc] : ThrownExceptions) { if (const auto *TD = T->getAsTagDecl()) { if (TD->getDeclName().isIdentifier()) { if ((IgnoreBadAlloc && @@ -396,7 +410,7 @@ } } for (const Type *T : TypesToDelete) - ThrownExceptions.erase(T); + ThrownExceptions.erase({T, SourceLocation()}); reevaluateBehaviour(); return *this; @@ -444,7 +458,8 @@ auto Result = ExceptionInfo::createUnknown(); if (const auto *FPT = Func->getType()->getAs()) { for (const QualType &Ex : FPT->exceptions()) - Result.registerException(Ex.getTypePtr()); + Result.registerException(Ex.getTypePtr(), + Func->getExceptionSpecSourceRange().getBegin()); } return Result; } @@ -467,12 +482,13 @@ ->getPointeeType() ->getUnqualifiedDesugaredType(); Results.registerException( - ThrownExpr->getType()->getUnqualifiedDesugaredType()); + ThrownExpr->getType()->getUnqualifiedDesugaredType(), + Throw->getThrowLoc()); } else // A rethrow of a caught exception happens which makes it possible // to throw all exception that are caught in the 'catch' clause of // the parent try-catch block. - Results.registerExceptions(Caught); + Results.registerExceptions(Caught, Throw->getThrowLoc()); } else if (const auto *Try = dyn_cast(St)) { ExceptionInfo Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack); @@ -498,10 +514,11 @@ // thrown types (because it's sensitive to inheritance) the throwing // situation changes. First of all filter the exception types and // analyze if the baseclass-exception is rethrown. - if (Uncaught.filterByCatch( - CaughtType, Catch->getExceptionDecl()->getASTContext())) { - ExceptionInfo::Throwables CaughtExceptions; - CaughtExceptions.insert(CaughtType); + ExceptionInfo::Throwables CaughtExceptions; + if (Uncaught.filterByCatch(CaughtType, + Catch->getExceptionDecl()->getASTContext(), + CaughtExceptions)) { + CaughtExceptions.insert({CaughtType, SourceLocation()}); ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(), CaughtExceptions, CallStack); Results.merge(Rethrown); @@ -532,7 +549,7 @@ ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack); Results.merge(throwsException(Coro->getExceptionHandler(), Excs.getExceptionTypes(), CallStack)); - for (const Type *Throwable : Excs.getExceptionTypes()) { + for (auto [Throwable, ThrowLoc] : Excs.getExceptionTypes()) { if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) { ExceptionInfo DestructorExcs = throwsException(ThrowableRec->getDestructor(), Caught, CallStack); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -253,8 +253,9 @@ Global options of the same name should be used instead. - Improved :doc:`bugprone-exception-escape - ` to not emit warnings for - forward declarations of functions. + ` check to not emit warnings for + forward declarations of functions. Emit additional diagnostic information + about uncaught exceptions. - Fixed :doc:`bugprone-exception-escape` for coroutines where previously a warning would be emitted with coroutines diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp @@ -15,6 +15,7 @@ int throwsAndCallsRethrower() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE+2]]:9: note: may throw 'int' here try { throw 1; } catch(...) { @@ -24,6 +25,7 @@ int throwsAndCallsCallsRethrower() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE+2]]:9: note: may throw 'int' here try { throw 1; } catch(...) { @@ -34,3 +36,18 @@ void rethrowerNoexcept() noexcept { throw; } + +void throwInt() { + throw 5; +} + +void rethrowInt() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'rethrowInt' which should not throw exceptions [bugprone-exception-escape] +// CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here + try { + throwInt(); + } catch(int) { + throw; + } catch(...) { + } +} 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 @@ -2,6 +2,7 @@ void throwing_throw_nothing() throw() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_throw_nothing' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here throw 1; } @@ -13,11 +14,14 @@ void indirect_implicit() throw() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_implicit' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here implicit_int_thrower(); } void indirect_explicit() throw() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_explicit' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE-14]]:29: note: may throw 'int' here +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: may throw unknown exceptions explicit_int_thrower(); } @@ -28,4 +32,5 @@ struct sub_throws : super_throws { 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 + // CHECK-MESSAGES: :[[@LINE-6]]:31: note: may throw 'int' here }; 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 @@ -9,6 +9,7 @@ struct throwing_destructor { ~throwing_destructor() { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function '~throwing_destructor' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here throw 1; } }; @@ -16,6 +17,7 @@ struct throwing_move_constructor { throwing_move_constructor(throwing_move_constructor&&) { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'throwing_move_constructor' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here throw 1; } }; @@ -23,12 +25,14 @@ struct throwing_move_assignment { throwing_move_assignment& operator=(throwing_move_assignment&&) { // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: an exception may be thrown in function 'operator=' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here throw 1; } }; void throwing_noexcept() noexcept { - // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here throw 1; } @@ -42,6 +46,7 @@ void throw_and_catch_some(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch_some' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'double' here try { if (n) throw 1; throw 1.1; @@ -70,6 +75,7 @@ void throw_and_rethrow() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_rethrow' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+2]]:5: note: may throw 'int' here try { throw 1; } catch(int &) { @@ -79,6 +85,7 @@ void throw_catch_throw() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_throw' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'int' here try { throw 1; } catch(int &) { @@ -88,6 +95,7 @@ void throw_catch_rethrow_the_rest(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_rethrow_the_rest' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'double' here try { if (n) throw 1; throw 1.1; @@ -120,6 +128,7 @@ void throw_catch_multi_ptr_1() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'char **' here try { char **p = 0; throw p; @@ -152,7 +161,7 @@ } // FIXME: In this case 'a' is convertible to the handler and should be caught -// but in reality it's thrown. Note that clang doesn't report a warning for +// but in reality it's thrown. Note that clang doesn't report a warning for // this either. void throw_catch_multi_ptr_5() noexcept { try { @@ -165,6 +174,7 @@ void throw_c_catch_pointer() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const int *' here try { int a = 1; const int *p = &a; @@ -174,6 +184,7 @@ void throw_c_catch_pointer_v() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const int *' here try { int a = 1; const int *p = &a; @@ -183,6 +194,7 @@ void throw_v_catch_pointer() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'volatile int *' here try { int a = 1; volatile int *p = &a; @@ -192,6 +204,7 @@ void throw_v_catch_pointer_c() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'volatile int *' here try { int a = 1; volatile int *p = &a; @@ -201,6 +214,7 @@ void throw_cv_catch_pointer_c() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const volatile int *' here try { int a = 1; const volatile int *p = &a; @@ -210,6 +224,7 @@ void throw_cv_catch_pointer_v() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const volatile int *' here try { int a = 1; const volatile int *p = &a; @@ -249,17 +264,18 @@ void throw_derived_catch_base_ptr_c() noexcept { try { derived d; - throw &d; + throw &d; } catch(const base *) { } } void throw_derived_catch_base_ptr() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const derived *' here try { derived d; const derived *p = &d; - throw p; + throw p; } catch(base *) { } } @@ -280,55 +296,61 @@ void throw_derived_catch_base_private() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'B' here try { B b; - throw b; + throw b; } catch(A) { } } void throw_derived_catch_base_private_ptr() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private_ptr' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'B *' here try { B b; - throw &b; + throw &b; } catch(A *) { } } void throw_derived_catch_base_protected() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'C' here try { C c; - throw c; + throw c; } catch(A) { } } void throw_derived_catch_base_protected_ptr() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected_ptr' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'C *' here try { C c; - throw &c; + throw &c; } catch(A *) { } } void throw_derived_catch_base_ambiguous() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'E' here try { E e; - throw e; + throw e; } catch(A) { } } void throw_derived_catch_base_ambiguous_ptr() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous_ptr' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'E *' here try { E e; - throw e; - } catch(A) { + throw &e; + } catch(A *) { } } @@ -344,6 +366,7 @@ void throw_alias_catch_original_warn() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_alias_catch_original_warn' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'float' here using alias = float; try { @@ -365,6 +388,7 @@ void throw_original_catch_alias_warn() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_original_catch_alias_warn' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'char **' here using alias = int; try { @@ -428,6 +452,7 @@ void throw_basefn_catch_derivedfn() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basefn_catch_derivedfn' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+2]]:5: note: may throw 'void (baseMember::*)()' here try { throw &baseMember::foo; } catch(void(derivedMember::*)()) { @@ -443,6 +468,7 @@ 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 + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *baseMember::**' here try { auto ptr = &baseMember::iptr; throw &ptr; @@ -460,6 +486,7 @@ void throw_basem_catch_derivedm() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basem_catch_derivedm' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *baseMember::**' here try { auto ptr = &baseMember::iptr; throw &ptr; @@ -469,6 +496,7 @@ void throw_derivedm_catch_basem() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derivedm_catch_basem' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *derivedMember::**' here try { int *derivedMember::* ptr = &derivedMember::iptr; throw &ptr; @@ -478,6 +506,7 @@ void throw_original_catch_alias_2_warn() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_original_catch_alias_2_warn' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'char **' here using alias = const int *const; try { @@ -501,6 +530,7 @@ void bad_try_nested_try(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_try_nested_try' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+2]]:12: note: may throw 'int' here try { if (n) throw 1; try { @@ -537,6 +567,7 @@ void bad_catch_nested_try() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_catch_nested_try' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+5]]:7: note: may throw 'double' here try { throw 1; } catch(int &) { @@ -558,11 +589,13 @@ void indirect_implicit() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_implicit' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-9]]:3: note: may throw 'int' here implicit_int_thrower(); } void indirect_explicit() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_explicit' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-11]]:3: note: may throw 'int' here explicit_int_thrower(); } @@ -583,6 +616,7 @@ void swap(int&, int&) { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'swap' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here throw 1; } @@ -601,11 +635,13 @@ void enabled1() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled1' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here throw 1; } void enabled2() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled2' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here enabled1(); } @@ -636,6 +672,7 @@ void this_counts(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'this_counts' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:10: note: may throw 'int' here if (n) throw 1; throw ignored1(); } @@ -646,6 +683,7 @@ int directly_recursive(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'directly_recursive' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here if (n == 0) thrower(n); return directly_recursive(n); @@ -659,6 +697,7 @@ int indirectly_recursive(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'indirectly_recursive' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-19]]:3: note: may throw 'int' here if (n == 0) thrower(n); return recursion_helper(n); @@ -671,6 +710,7 @@ struct sub_throws : super_throws { sub_throws() noexcept : super_throws() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-6]]:36: note: may throw 'int' here }; struct init_member_throws { @@ -678,6 +718,7 @@ init_member_throws() noexcept : s() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'init_member_throws' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-14]]:36: note: may throw 'int' here }; struct implicit_init_member_throws { @@ -685,6 +726,7 @@ implicit_init_member_throws() noexcept {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'implicit_init_member_throws' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-22]]:36: note: may throw 'int' here }; struct init { @@ -696,10 +738,35 @@ in_class_init_throws() noexcept {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'in_class_init_throws' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-8]]:45: note: may throw 'int' here }; int main() { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'main' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here throw 1; return 0; } + +struct Exception1 {}; +struct Exception2 {}; + +void throwException1Or2(bool value) noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwException1Or2' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'Exception1' here +// CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'Exception2' here + if (value) + throw Exception1(); + else + throw Exception2(); +} + +void throwSomething() noexcept(false); + +void throwKnownAndUnknownException() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwKnownAndUnknownException' which should not throw exceptions +// CHECK-MESSAGES: :[[@LINE+3]]:3: note: may throw 'Exception1' here +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: may throw unknown exceptions + throwSomething(); + throw Exception1(); +}