ExceptionAnalyzer.h 6.1 KB

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