blob: 2ae08c83f5240dffb6a5da1aa162c90520cb9572 [file] [log] [blame]
Olivier Deprezf4ef2d02021-04-20 13:36:24 +02001//===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
11
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "llvm/ADT/SmallSet.h"
15#include "llvm/ADT/StringSet.h"
16
17namespace clang {
18namespace tidy {
19namespace utils {
20
21/// This class analysis if a `FunctionDecl` can in principle throw an
22/// exception, either directly or indirectly. It can be configured to ignore
23/// custom exception types.
24class ExceptionAnalyzer {
25public:
26 enum class State : std::int8_t {
27 Throwing = 0, ///< The function can definitely throw given an AST.
28 NotThrowing = 1, ///< This function can not throw, given an AST.
29 Unknown = 2, ///< This can happen for extern functions without available
30 ///< definition.
31 };
32
33 /// Bundle the gathered information about an entity like a function regarding
34 /// it's exception behaviour. The 'NonThrowing'-state can be considered as the
35 /// neutral element in terms of information propagation.
36 /// In the case of 'Throwing' state it is possible that 'getExceptionTypes'
37 /// does not include *ALL* possible types as there is the possibility that
38 /// an 'Unknown' function is called that might throw a previously unknown
39 /// exception at runtime.
40 class ExceptionInfo {
41 public:
42 using Throwables = llvm::SmallSet<const Type *, 2>;
43 static ExceptionInfo createUnknown() {
44 return ExceptionInfo(State::Unknown);
45 }
46 static ExceptionInfo createNonThrowing() {
47 return ExceptionInfo(State::Throwing);
48 }
49
50 /// By default the exception situation is unknown and must be
51 /// clarified step-wise.
52 ExceptionInfo() : Behaviour(State::NotThrowing), ContainsUnknown(false) {}
53 ExceptionInfo(State S)
54 : Behaviour(S), ContainsUnknown(S == State::Unknown) {}
55
56 ExceptionInfo(const ExceptionInfo &) = default;
57 ExceptionInfo &operator=(const ExceptionInfo &) = default;
58 ExceptionInfo(ExceptionInfo &&) = default;
59 ExceptionInfo &operator=(ExceptionInfo &&) = default;
60
61 State getBehaviour() const { return Behaviour; }
62
63 /// Register a single exception type as recognized potential exception to be
64 /// thrown.
65 void registerException(const Type *ExceptionType);
66
67 /// Registers a `SmallVector` of exception types as recognized potential
68 /// exceptions to be thrown.
69 void registerExceptions(const Throwables &Exceptions);
70
71 /// Updates the local state according to the other state. That means if
72 /// for example a function contains multiple statements the 'ExceptionInfo'
73 /// for the final function is the merged result of each statement.
74 /// If one of these statements throws the whole function throws and if one
75 /// part is unknown and the rest is non-throwing the result will be
76 /// unknown.
77 ExceptionInfo &merge(const ExceptionInfo &Other);
78
79 /// This method is useful in case 'catch' clauses are analyzed as it is
80 /// possible to catch multiple exception types by one 'catch' if they
81 /// are a subclass of the 'catch'ed exception type.
82 /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
83 bool filterByCatch(const Type *BaseClass);
84
85 /// Filter the set of thrown exception type against a set of ignored
86 /// types that shall not be considered in the exception analysis.
87 /// This includes explicit `std::bad_alloc` ignoring as separate option.
88 ExceptionInfo &
89 filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes,
90 bool IgnoreBadAlloc);
91
92 /// Clear the state to 'NonThrowing' to make the corresponding entity
93 /// neutral.
94 void clear();
95
96 /// References the set of known exception types that can escape from the
97 /// corresponding entity.
98 const Throwables &getExceptionTypes() const { return ThrownExceptions; }
99
100 /// Signal if the there is any 'Unknown' element within the scope of
101 /// the related entity. This might be relevant if the entity is 'Throwing'
102 /// and to ensure that no other exception then 'getExceptionTypes' can
103 /// occur. If there is an 'Unknown' element this can not be guaranteed.
104 bool containsUnknownElements() const { return ContainsUnknown; }
105
106 private:
107 /// Recalculate the 'Behaviour' for example after filtering.
108 void reevaluateBehaviour();
109
110 /// Keep track if the entity related to this 'ExceptionInfo' can in princple
111 /// throw, if it's unknown or if it won't throw.
112 State Behaviour;
113
114 /// Keep track if the entity contains any unknown elements to keep track
115 /// of the certainty of decisions and/or correct 'Behaviour' transition
116 /// after filtering.
117 bool ContainsUnknown;
118
119 /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or
120 /// 'Unknown'.
121 Throwables ThrownExceptions;
122 };
123
124 ExceptionAnalyzer() = default;
125
126 void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; }
127 void ignoreExceptions(llvm::StringSet<> ExceptionNames) {
128 IgnoredExceptions = std::move(ExceptionNames);
129 }
130
131 ExceptionInfo analyze(const FunctionDecl *Func);
132 ExceptionInfo analyze(const Stmt *Stmt);
133
134private:
135 ExceptionInfo
136 throwsException(const FunctionDecl *Func,
137 llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
138 ExceptionInfo
139 throwsException(const Stmt *St, const ExceptionInfo::Throwables &Caught,
140 llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
141
142 ExceptionInfo analyzeImpl(const FunctionDecl *Func);
143 ExceptionInfo analyzeImpl(const Stmt *Stmt);
144
145 template <typename T> ExceptionInfo analyzeDispatch(const T *Node);
146
147 bool IgnoreBadAlloc = true;
148 llvm::StringSet<> IgnoredExceptions;
149 std::map<const FunctionDecl *, ExceptionInfo> FunctionCache;
150};
151
152} // namespace utils
153} // namespace tidy
154} // namespace clang
155
156#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H