11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/StringSet.h"
20 bugprone::ExceptionEscapeCheck::TreatFunctionsWithoutSpecification> {
24 static llvm::ArrayRef<
25 std::pair<TreatFunctionsWithoutSpecification, StringRef>>
27 static constexpr std::pair<TreatFunctionsWithoutSpecification, StringRef>
29 {TreatFunctionsWithoutSpecification::None,
"None"},
30 {TreatFunctionsWithoutSpecification::OnlyUndefined,
32 {TreatFunctionsWithoutSpecification::All,
"All"},
41AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
42 FunctionsThatShouldNotThrow) {
43 return FunctionsThatShouldNotThrow.contains(Node.getNameAsString());
46AST_MATCHER(FunctionDecl, isExplicitThrow) {
47 return isExplicitThrowExceptionSpec(Node.getExceptionSpecType()) &&
48 Node.getExceptionSpecSourceRange().isValid();
52 return Node.getNumParams() > 0;
59 :
ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
60 "FunctionsThatShouldNotThrow",
"")),
61 RawIgnoredExceptions(Options.get(
"IgnoredExceptions",
"")),
62 RawCheckedSwapFunctions(
63 Options.get(
"CheckedSwapFunctions",
"swap,iter_swap,iter_move")),
64 CheckDestructors(Options.get(
"CheckDestructors", true)),
65 CheckMoveMemberFunctions(Options.get(
"CheckMoveMemberFunctions", true)),
66 CheckMain(Options.get(
"CheckMain", true)),
67 CheckNothrowFunctions(Options.get(
"CheckNothrowFunctions", true)),
68 TreatFunctionsWithoutSpecificationAsThrowing(
69 Options.get(
"TreatFunctionsWithoutSpecificationAsThrowing",
71 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
72 IgnoredExceptionsVec, CheckedSwapFunctionsVec;
73 RawFunctionsThatShouldNotThrow.split(FunctionsThatShouldNotThrowVec,
",", -1,
75 FunctionsThatShouldNotThrow.insert_range(FunctionsThatShouldNotThrowVec);
77 RawCheckedSwapFunctions.split(CheckedSwapFunctionsVec,
",", -1,
false);
78 CheckedSwapFunctions.insert_range(CheckedSwapFunctionsVec);
80 llvm::StringSet<> IgnoredExceptions;
81 RawIgnoredExceptions.split(IgnoredExceptionsVec,
",", -1,
false);
82 IgnoredExceptions.insert_range(IgnoredExceptionsVec);
83 Tracer.ignoreExceptions(std::move(IgnoredExceptions));
84 Tracer.ignoreBadAlloc(
true);
86 Tracer.assumeMissingDefinitionsFunctionsAsThrowing(
87 TreatFunctionsWithoutSpecificationAsThrowing !=
90 Tracer.assumeUnannotatedFunctionsAsThrowing(
91 TreatFunctionsWithoutSpecificationAsThrowing ==
96 Options.store(Opts,
"FunctionsThatShouldNotThrow",
97 RawFunctionsThatShouldNotThrow);
98 Options.store(Opts,
"IgnoredExceptions", RawIgnoredExceptions);
99 Options.store(Opts,
"CheckedSwapFunctions", RawCheckedSwapFunctions);
100 Options.store(Opts,
"CheckDestructors", CheckDestructors);
101 Options.store(Opts,
"CheckMoveMemberFunctions", CheckMoveMemberFunctions);
102 Options.store(Opts,
"CheckMain", CheckMain);
103 Options.store(Opts,
"CheckNothrowFunctions", CheckNothrowFunctions);
104 Options.store(Opts,
"TreatFunctionsWithoutSpecificationAsThrowing",
105 TreatFunctionsWithoutSpecificationAsThrowing);
109 auto MatchIf = [](
bool Enabled,
const auto &Matcher) {
110 const ast_matchers::internal::Matcher<FunctionDecl> Nothing =
112 return Enabled ? Matcher : Nothing;
118 MatchIf(CheckNothrowFunctions, isNoThrow()),
119 allOf(anyOf(MatchIf(CheckDestructors, cxxDestructorDecl()),
121 CheckMoveMemberFunctions,
122 anyOf(cxxConstructorDecl(isMoveConstructor()),
123 cxxMethodDecl(isMoveAssignmentOperator()))),
124 MatchIf(CheckMain, isMain()),
125 allOf(isEnabled(CheckedSwapFunctions),
126 hasAtLeastOneParameter())),
127 unless(isExplicitThrow())),
128 isEnabled(FunctionsThatShouldNotThrow)))
134 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>(
"thrower");
139 const utils::ExceptionAnalyzer::ExceptionInfo Info =
145 diag(MatchedDecl->getLocation(),
"an exception may be thrown in function "
146 "%0 which should not throw exceptions")
149 if (Info.getExceptions().empty())
152 const auto &[ThrowType, ThrowInfo] = *Info.getExceptions().begin();
154 if (ThrowInfo.Loc.isInvalid())
159 "frame #0: unhandled exception of type %0 may be thrown in function %1 "
162 << QualType(ThrowType, 0U) << Stack.back().first;
165 for (
auto CurrIt = ++Stack.rbegin(), PrevIt = Stack.rbegin();
166 CurrIt != Stack.rend(); ++CurrIt, ++PrevIt) {
167 const FunctionDecl *CurrFunction = CurrIt->first;
168 const FunctionDecl *PrevFunction = PrevIt->first;
169 const SourceLocation PrevLocation = PrevIt->second;
170 if (PrevLocation.isValid()) {
171 diag(PrevLocation,
"frame #%0: function %1 calls function %2 here",
173 << FrameNo << CurrFunction << PrevFunction;
175 diag(CurrFunction->getLocation(),
176 "frame #%0: function %1 calls function %2", DiagnosticIDs::Note)
177 << FrameNo << CurrFunction << PrevFunction;
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
TreatFunctionsWithoutSpecification
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
@ Throwing
The function can definitely throw given an AST.
llvm::MapVector< const FunctionDecl *, SourceLocation > CallStack
We use a MapVector to preserve the order of the functions in the call stack as well as have fast look...
ExceptionInfo analyze(const FunctionDecl *Func)
AST_MATCHER(BinaryOperator, isRelationalOperator)
llvm::StringMap< ClangTidyValue > OptionMap
bugprone::ExceptionEscapeCheck::TreatFunctionsWithoutSpecification TreatFunctionsWithoutSpecification
static llvm::ArrayRef< std::pair< TreatFunctionsWithoutSpecification, StringRef > > getEnumMapping()
This class should be specialized by any enum type that needs to be converted to and from an llvm::Str...