| //===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H |
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/ADT/StringSet.h" |
| |
| namespace clang { |
| namespace tidy { |
| namespace utils { |
| |
| /// This class analysis if a `FunctionDecl` can in principle throw an |
| /// exception, either directly or indirectly. It can be configured to ignore |
| /// custom exception types. |
| class ExceptionAnalyzer { |
| public: |
| enum class State : std::int8_t { |
| Throwing = 0, ///< The function can definitely throw given an AST. |
| NotThrowing = 1, ///< This function can not throw, given an AST. |
| Unknown = 2, ///< This can happen for extern functions without available |
| ///< definition. |
| }; |
| |
| /// Bundle the gathered information about an entity like a function regarding |
| /// it's exception behaviour. The 'NonThrowing'-state can be considered as the |
| /// neutral element in terms of information propagation. |
| /// In the case of 'Throwing' state it is possible that 'getExceptionTypes' |
| /// does not include *ALL* possible types as there is the possibility that |
| /// an 'Unknown' function is called that might throw a previously unknown |
| /// exception at runtime. |
| class ExceptionInfo { |
| public: |
| using Throwables = llvm::SmallSet<const Type *, 2>; |
| static ExceptionInfo createUnknown() { |
| return ExceptionInfo(State::Unknown); |
| } |
| static ExceptionInfo createNonThrowing() { |
| return ExceptionInfo(State::Throwing); |
| } |
| |
| /// By default the exception situation is unknown and must be |
| /// clarified step-wise. |
| ExceptionInfo() : Behaviour(State::NotThrowing), ContainsUnknown(false) {} |
| ExceptionInfo(State S) |
| : Behaviour(S), ContainsUnknown(S == State::Unknown) {} |
| |
| ExceptionInfo(const ExceptionInfo &) = default; |
| ExceptionInfo &operator=(const ExceptionInfo &) = default; |
| ExceptionInfo(ExceptionInfo &&) = default; |
| ExceptionInfo &operator=(ExceptionInfo &&) = default; |
| |
| State getBehaviour() const { return Behaviour; } |
| |
| /// Register a single exception type as recognized potential exception to be |
| /// thrown. |
| void registerException(const Type *ExceptionType); |
| |
| /// Registers a `SmallVector` of exception types as recognized potential |
| /// exceptions to be thrown. |
| void registerExceptions(const Throwables &Exceptions); |
| |
| /// Updates the local state according to the other state. That means if |
| /// for example a function contains multiple statements the 'ExceptionInfo' |
| /// for the final function is the merged result of each statement. |
| /// If one of these statements throws the whole function throws and if one |
| /// part is unknown and the rest is non-throwing the result will be |
| /// unknown. |
| ExceptionInfo &merge(const ExceptionInfo &Other); |
| |
| /// This method is useful in case 'catch' clauses are analyzed as it is |
| /// possible to catch multiple exception types by one 'catch' if they |
| /// are a subclass of the 'catch'ed exception type. |
| /// Returns 'true' if some exceptions were filtered, otherwise 'false'. |
| bool filterByCatch(const Type *BaseClass); |
| |
| /// Filter the set of thrown exception type against a set of ignored |
| /// types that shall not be considered in the exception analysis. |
| /// This includes explicit `std::bad_alloc` ignoring as separate option. |
| ExceptionInfo & |
| filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes, |
| bool IgnoreBadAlloc); |
| |
| /// Clear the state to 'NonThrowing' to make the corresponding entity |
| /// neutral. |
| void clear(); |
| |
| /// References the set of known exception types that can escape from the |
| /// corresponding entity. |
| const Throwables &getExceptionTypes() const { return ThrownExceptions; } |
| |
| /// Signal if the there is any 'Unknown' element within the scope of |
| /// the related entity. This might be relevant if the entity is 'Throwing' |
| /// and to ensure that no other exception then 'getExceptionTypes' can |
| /// occur. If there is an 'Unknown' element this can not be guaranteed. |
| bool containsUnknownElements() const { return ContainsUnknown; } |
| |
| private: |
| /// Recalculate the 'Behaviour' for example after filtering. |
| void reevaluateBehaviour(); |
| |
| /// Keep track if the entity related to this 'ExceptionInfo' can in princple |
| /// throw, if it's unknown or if it won't throw. |
| State Behaviour; |
| |
| /// Keep track if the entity contains any unknown elements to keep track |
| /// of the certainty of decisions and/or correct 'Behaviour' transition |
| /// after filtering. |
| bool ContainsUnknown; |
| |
| /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or |
| /// 'Unknown'. |
| Throwables ThrownExceptions; |
| }; |
| |
| ExceptionAnalyzer() = default; |
| |
| void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; } |
| void ignoreExceptions(llvm::StringSet<> ExceptionNames) { |
| IgnoredExceptions = std::move(ExceptionNames); |
| } |
| |
| ExceptionInfo analyze(const FunctionDecl *Func); |
| ExceptionInfo analyze(const Stmt *Stmt); |
| |
| private: |
| ExceptionInfo |
| throwsException(const FunctionDecl *Func, |
| llvm::SmallSet<const FunctionDecl *, 32> &CallStack); |
| ExceptionInfo |
| throwsException(const Stmt *St, const ExceptionInfo::Throwables &Caught, |
| llvm::SmallSet<const FunctionDecl *, 32> &CallStack); |
| |
| ExceptionInfo analyzeImpl(const FunctionDecl *Func); |
| ExceptionInfo analyzeImpl(const Stmt *Stmt); |
| |
| template <typename T> ExceptionInfo analyzeDispatch(const T *Node); |
| |
| bool IgnoreBadAlloc = true; |
| llvm::StringSet<> IgnoredExceptions; |
| std::map<const FunctionDecl *, ExceptionInfo> FunctionCache; |
| }; |
| |
| } // namespace utils |
| } // namespace tidy |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H |