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 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 17 namespace clang { 18 namespace tidy { 19 namespace 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. 24 class ExceptionAnalyzer { 25 public: 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>; createUnknown()43 static ExceptionInfo createUnknown() { 44 return ExceptionInfo(State::Unknown); 45 } createNonThrowing()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. ExceptionInfo()52 ExceptionInfo() : Behaviour(State::NotThrowing), ContainsUnknown(false) {} ExceptionInfo(State S)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 getBehaviour()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. getExceptionTypes()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. containsUnknownElements()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 ignoreBadAlloc(bool ShallIgnore)126 void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; } ignoreExceptions(llvm::StringSet<> ExceptionNames)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 134 private: 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 157