ExceptionAnalyzer.cpp 9.8 KB

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