PiotrZSL updated this revision to Diff 532783.
PiotrZSL marked 3 inline comments as done.
PiotrZSL added a comment.
Review fixes.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D153298/new/
https://reviews.llvm.org/D153298
Files:
clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ 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 here
+ throwSomething();
+ throw Exception1();
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
+++ 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 here
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
};
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
+++ 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(...) {
+ }
+}
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ 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
- <clang-tidy/checks/bugprone/exception-escape>` to not emit warnings for
- forward declarations of functions.
+ <clang-tidy/checks/bugprone/exception-escape>` check to not emit warnings for
+ forward declarations of functions. Emit additional diagnostic information
+ about uncaught exceptions.
- Fixed :doc:`bugprone-exception-escape<clang-tidy/checks/bugprone/exception-escape>`
for coroutines where previously a warning would be emitted with coroutines
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ 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 <functional>
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<const Type *, 2>;
+ struct ThrowInfo {
+ const Type *ThrowType;
+ SourceLocation ThrowLoc;
+
+ bool operator<(const ThrowInfo &R) const noexcept {
+ return ThrowType < R.ThrowType;
+ }
+
+ bool operator==(const ThrowInfo &R) const noexcept {
+ return ThrowType == R.ThrowType;
+ }
+ };
+
+ using Throwables = llvm::SmallSet<ThrowInfo, 2>;
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.
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ 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<const Type *, 8> 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<const Type *, 8> 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<FunctionProtoType>()) {
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<CXXTryStmt>(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);
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ 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 here",
+ DiagnosticIDs::Note);
}
} // namespace tidy::bugprone
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits