clang-tools  10.0.0
ExceptionAnalyzer.cpp
Go to the documentation of this file.
1 //===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===//
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 #include "ExceptionAnalyzer.h"
10 
11 namespace clang {
12 namespace tidy {
13 namespace utils {
14 
16  const Type *ExceptionType) {
17  assert(ExceptionType != nullptr && "Only valid types are accepted");
18  Behaviour = State::Throwing;
19  ThrownExceptions.insert(ExceptionType);
20 }
21 
23  const Throwables &Exceptions) {
24  if (Exceptions.size() == 0)
25  return;
26  Behaviour = State::Throwing;
27  ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
28 }
29 
31  const ExceptionAnalyzer::ExceptionInfo &Other) {
32  // Only the following two cases require an update to the local
33  // 'Behaviour'. If the local entity is already throwing there will be no
34  // change and if the other entity is throwing the merged entity will throw
35  // as well.
36  // If one of both entities is 'Unknown' and the other one does not throw
37  // the merged entity is 'Unknown' as well.
38  if (Other.Behaviour == State::Throwing)
39  Behaviour = State::Throwing;
40  else if (Other.Behaviour == State::Unknown && Behaviour == State::NotThrowing)
41  Behaviour = State::Unknown;
42 
43  ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
44  ThrownExceptions.insert(Other.ThrownExceptions.begin(),
45  Other.ThrownExceptions.end());
46  return *this;
47 }
48 
49 static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
50  const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
51  const auto *BaseClass = BaseType->getAsCXXRecordDecl();
52  if (!DerivedClass || !BaseClass)
53  return false;
54 
55  return !DerivedClass->forallBases(
56  [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
57 }
58 
60  llvm::SmallVector<const Type *, 8> TypesToDelete;
61  for (const Type *T : ThrownExceptions) {
62  if (T == BaseClass || isBaseOf(T, BaseClass))
63  TypesToDelete.push_back(T);
64  }
65 
66  for (const Type *T : TypesToDelete)
67  ThrownExceptions.erase(T);
68 
69  reevaluateBehaviour();
70  return TypesToDelete.size() > 0;
71 }
72 
75  const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) {
76  llvm::SmallVector<const Type *, 8> TypesToDelete;
77  // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible.
78  // Therefore this slightly hacky implementation is required.
79  for (const Type *T : ThrownExceptions) {
80  if (const auto *TD = T->getAsTagDecl()) {
81  if (TD->getDeclName().isIdentifier()) {
82  if ((IgnoreBadAlloc &&
83  (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
84  (IgnoredTypes.count(TD->getName()) > 0))
85  TypesToDelete.push_back(T);
86  }
87  }
88  }
89  for (const Type *T : TypesToDelete)
90  ThrownExceptions.erase(T);
91 
92  reevaluateBehaviour();
93  return *this;
94 }
95 
97  Behaviour = State::NotThrowing;
98  ContainsUnknown = false;
99  ThrownExceptions.clear();
100 }
101 
102 void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
103  if (ThrownExceptions.size() == 0)
104  if (ContainsUnknown)
105  Behaviour = State::Unknown;
106  else
107  Behaviour = State::NotThrowing;
108  else
109  Behaviour = State::Throwing;
110 }
111 
112 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
113  const FunctionDecl *Func,
114  llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
115  if (CallStack.count(Func))
117 
118  if (const Stmt *Body = Func->getBody()) {
119  CallStack.insert(Func);
120  ExceptionInfo Result =
121  throwsException(Body, ExceptionInfo::Throwables(), CallStack);
122  CallStack.erase(Func);
123  return Result;
124  }
125 
126  auto Result = ExceptionInfo::createUnknown();
127  if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
128  for (const QualType &Ex : FPT->exceptions())
129  Result.registerException(Ex.getTypePtr());
130  }
131  return Result;
132 }
133 
134 /// Analyzes a single statment on it's throwing behaviour. This is in principle
135 /// possible except some 'Unknown' functions are called.
136 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
137  const Stmt *St, const ExceptionInfo::Throwables &Caught,
138  llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
140  if (!St)
141  return Results;
142 
143  if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
144  if (const auto *ThrownExpr = Throw->getSubExpr()) {
145  const auto *ThrownType =
146  ThrownExpr->getType()->getUnqualifiedDesugaredType();
147  if (ThrownType->isReferenceType())
148  ThrownType = ThrownType->castAs<ReferenceType>()
149  ->getPointeeType()
150  ->getUnqualifiedDesugaredType();
151  Results.registerException(
152  ThrownExpr->getType()->getUnqualifiedDesugaredType());
153  } else
154  // A rethrow of a caught exception happens which makes it possible
155  // to throw all exception that are caught in the 'catch' clause of
156  // the parent try-catch block.
157  Results.registerExceptions(Caught);
158  } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
159  ExceptionInfo Uncaught =
160  throwsException(Try->getTryBlock(), Caught, CallStack);
161  for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
162  const CXXCatchStmt *Catch = Try->getHandler(i);
163 
164  // Everything is catched through 'catch(...)'.
165  if (!Catch->getExceptionDecl()) {
166  ExceptionInfo Rethrown = throwsException(
167  Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
168  Results.merge(Rethrown);
169  Uncaught.clear();
170  } else {
171  const auto *CaughtType =
172  Catch->getCaughtType()->getUnqualifiedDesugaredType();
173  if (CaughtType->isReferenceType()) {
174  CaughtType = CaughtType->castAs<ReferenceType>()
175  ->getPointeeType()
176  ->getUnqualifiedDesugaredType();
177  }
178 
179  // If the caught exception will catch multiple previously potential
180  // thrown types (because it's sensitive to inheritance) the throwing
181  // situation changes. First of all filter the exception types and
182  // analyze if the baseclass-exception is rethrown.
183  if (Uncaught.filterByCatch(CaughtType)) {
184  ExceptionInfo::Throwables CaughtExceptions;
185  CaughtExceptions.insert(CaughtType);
186  ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
187  CaughtExceptions, CallStack);
188  Results.merge(Rethrown);
189  }
190  }
191  }
192  Results.merge(Uncaught);
193  } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
194  if (const FunctionDecl *Func = Call->getDirectCallee()) {
195  ExceptionInfo Excs = throwsException(Func, CallStack);
196  Results.merge(Excs);
197  }
198  } else {
199  for (const Stmt *Child : St->children()) {
200  ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
201  Results.merge(Excs);
202  }
203  }
204  return Results;
205 }
206 
208 ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
209  ExceptionInfo ExceptionList;
210 
211  // Check if the function has already been analyzed and reuse that result.
212  if (FunctionCache.count(Func) == 0) {
213  llvm::SmallSet<const FunctionDecl *, 32> CallStack;
214  ExceptionList = throwsException(Func, CallStack);
215 
216  // Cache the result of the analysis. This is done prior to filtering
217  // because it is best to keep as much information as possible.
218  // The results here might be relevant to different analysis passes
219  // with different needs as well.
220  FunctionCache.insert(std::make_pair(Func, ExceptionList));
221  } else
222  ExceptionList = FunctionCache[Func];
223 
224  return ExceptionList;
225 }
226 
228 ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
229  llvm::SmallSet<const FunctionDecl *, 32> CallStack;
230  return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
231 }
232 
233 template <typename T>
235 ExceptionAnalyzer::analyzeDispatch(const T *Node) {
236  ExceptionInfo ExceptionList = analyzeImpl(Node);
237 
238  if (ExceptionList.getBehaviour() == State::NotThrowing ||
239  ExceptionList.getBehaviour() == State::Unknown)
240  return ExceptionList;
241 
242  // Remove all ignored exceptions from the list of exceptions that can be
243  // thrown.
244  ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
245 
246  return ExceptionList;
247 }
248 
250 ExceptionAnalyzer::analyze(const FunctionDecl *Func) {
251  return analyzeDispatch(Func);
252 }
253 
255 ExceptionAnalyzer::analyze(const Stmt *Stmt) {
256  return analyzeDispatch(Stmt);
257 }
258 
259 } // namespace utils
260 } // namespace tidy
261 
262 } // namespace clang
Bundle the gathered information about an entity like a function regarding it&#39;s exception behaviour...
void registerExceptions(const Throwables &Exceptions)
Registers a SmallVector of exception types as recognized potential exceptions to be thrown...
void registerException(const Type *ExceptionType)
Register a single exception type as recognized potential exception to be thrown.
std::vector< CodeCompletionResult > Results
ExceptionInfo & merge(const ExceptionInfo &Other)
Updates the local state according to the other state.
This function can not throw, given an AST.
This can happen for extern functions without available definition.
const Throwables & getExceptionTypes() const
References the set of known exception types that can escape from the corresponding entity...
static bool isBaseOf(const Type *DerivedType, const Type *BaseType)
bool filterByCatch(const Type *BaseClass)
This method is useful in case &#39;catch&#39; clauses are analyzed as it is possible to catch multiple except...
The function can definitely throw given an AST.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ExceptionInfo analyze(const FunctionDecl *Func)
void clear()
Clear the state to &#39;NonThrowing&#39; to make the corresponding entity neutral.
ExceptionInfo & filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc)
Filter the set of thrown exception type against a set of ignored types that shall not be considered i...
NodeType Type