1 //===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "llvm/ADT/SmallSet.h"
15 #include "llvm/ADT/StringSet.h"
17 namespace clang::tidy::utils
{
19 /// This class analysis if a `FunctionDecl` can in principle throw an
20 /// exception, either directly or indirectly. It can be configured to ignore
21 /// custom exception types.
22 class ExceptionAnalyzer
{
25 Throwing
, ///< The function can definitely throw given an AST.
26 NotThrowing
, ///< This function can not throw, given an AST.
27 Unknown
, ///< This can happen for extern functions without available
31 /// Bundle the gathered information about an entity like a function regarding
32 /// it's exception behaviour. The 'NonThrowing'-state can be considered as the
33 /// neutral element in terms of information propagation.
34 /// In the case of 'Throwing' state it is possible that 'getExceptionTypes'
35 /// does not include *ALL* possible types as there is the possibility that
36 /// an 'Unknown' function is called that might throw a previously unknown
37 /// exception at runtime.
40 using Throwables
= llvm::SmallSet
<const Type
*, 2>;
41 static ExceptionInfo
createUnknown() {
42 return ExceptionInfo(State::Unknown
);
44 static ExceptionInfo
createNonThrowing() {
45 return ExceptionInfo(State::Throwing
);
48 /// By default the exception situation is unknown and must be
49 /// clarified step-wise.
50 ExceptionInfo() : Behaviour(State::NotThrowing
), ContainsUnknown(false) {}
51 ExceptionInfo(State S
)
52 : Behaviour(S
), ContainsUnknown(S
== State::Unknown
) {}
54 ExceptionInfo(const ExceptionInfo
&) = default;
55 ExceptionInfo
&operator=(const ExceptionInfo
&) = default;
56 ExceptionInfo(ExceptionInfo
&&) = default;
57 ExceptionInfo
&operator=(ExceptionInfo
&&) = default;
59 State
getBehaviour() const { return Behaviour
; }
61 /// Register a single exception type as recognized potential exception to be
63 void registerException(const Type
*ExceptionType
);
65 /// Registers a `SmallVector` of exception types as recognized potential
66 /// exceptions to be thrown.
67 void registerExceptions(const Throwables
&Exceptions
);
69 /// Updates the local state according to the other state. That means if
70 /// for example a function contains multiple statements the 'ExceptionInfo'
71 /// for the final function is the merged result of each statement.
72 /// If one of these statements throws the whole function throws and if one
73 /// part is unknown and the rest is non-throwing the result will be
75 ExceptionInfo
&merge(const ExceptionInfo
&Other
);
77 /// This method is useful in case 'catch' clauses are analyzed as it is
78 /// possible to catch multiple exception types by one 'catch' if they
79 /// are a subclass of the 'catch'ed exception type.
80 /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
81 bool filterByCatch(const Type
*BaseClass
, const ASTContext
&Context
);
83 /// Filter the set of thrown exception type against a set of ignored
84 /// types that shall not be considered in the exception analysis.
85 /// This includes explicit `std::bad_alloc` ignoring as separate option.
87 filterIgnoredExceptions(const llvm::StringSet
<> &IgnoredTypes
,
90 /// Clear the state to 'NonThrowing' to make the corresponding entity
94 /// References the set of known exception types that can escape from the
95 /// corresponding entity.
96 const Throwables
&getExceptionTypes() const { return ThrownExceptions
; }
98 /// Signal if the there is any 'Unknown' element within the scope of
99 /// the related entity. This might be relevant if the entity is 'Throwing'
100 /// and to ensure that no other exception then 'getExceptionTypes' can
101 /// occur. If there is an 'Unknown' element this can not be guaranteed.
102 bool containsUnknownElements() const { return ContainsUnknown
; }
105 /// Recalculate the 'Behaviour' for example after filtering.
106 void reevaluateBehaviour();
108 /// Keep track if the entity related to this 'ExceptionInfo' can in princple
109 /// throw, if it's unknown or if it won't throw.
112 /// Keep track if the entity contains any unknown elements to keep track
113 /// of the certainty of decisions and/or correct 'Behaviour' transition
115 bool ContainsUnknown
;
117 /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or
119 Throwables ThrownExceptions
;
122 ExceptionAnalyzer() = default;
124 void ignoreBadAlloc(bool ShallIgnore
) { IgnoreBadAlloc
= ShallIgnore
; }
125 void ignoreExceptions(llvm::StringSet
<> ExceptionNames
) {
126 IgnoredExceptions
= std::move(ExceptionNames
);
129 ExceptionInfo
analyze(const FunctionDecl
*Func
);
130 ExceptionInfo
analyze(const Stmt
*Stmt
);
134 throwsException(const FunctionDecl
*Func
,
135 const ExceptionInfo::Throwables
&Caught
,
136 llvm::SmallSet
<const FunctionDecl
*, 32> &CallStack
);
138 throwsException(const Stmt
*St
, const ExceptionInfo::Throwables
&Caught
,
139 llvm::SmallSet
<const FunctionDecl
*, 32> &CallStack
);
141 ExceptionInfo
analyzeImpl(const FunctionDecl
*Func
);
142 ExceptionInfo
analyzeImpl(const Stmt
*Stmt
);
144 template <typename T
> ExceptionInfo
analyzeDispatch(const T
*Node
);
146 bool IgnoreBadAlloc
= true;
147 llvm::StringSet
<> IgnoredExceptions
;
148 llvm::DenseMap
<const FunctionDecl
*, ExceptionInfo
> FunctionCache
{32u};
151 } // namespace clang::tidy::utils
153 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H