Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:36:22

0001 //===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
0010 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
0011 
0012 #include "clang/AST/ASTContext.h"
0013 #include "clang/ASTMatchers/ASTMatchFinder.h"
0014 #include "llvm/ADT/SmallSet.h"
0015 #include "llvm/ADT/StringSet.h"
0016 
0017 namespace clang::tidy::utils {
0018 
0019 /// This class analysis if a `FunctionDecl` can in principle throw an
0020 /// exception, either directly or indirectly. It can be configured to ignore
0021 /// custom exception types.
0022 class ExceptionAnalyzer {
0023 public:
0024   enum class State {
0025     Throwing,    ///< The function can definitely throw given an AST.
0026     NotThrowing, ///< This function can not throw, given an AST.
0027     Unknown,     ///< This can happen for extern functions without available
0028                  ///< definition.
0029   };
0030 
0031   /// Bundle the gathered information about an entity like a function regarding
0032   /// it's exception behaviour. The 'NonThrowing'-state can be considered as the
0033   /// neutral element in terms of information propagation.
0034   /// In the case of 'Throwing' state it is possible that 'getExceptionTypes'
0035   /// does not include *ALL* possible types as there is the possibility that
0036   /// an 'Unknown' function is called that might throw a previously unknown
0037   /// exception at runtime.
0038   class ExceptionInfo {
0039   public:
0040     using Throwables = llvm::SmallSet<const Type *, 2>;
0041     static ExceptionInfo createUnknown() { return {State::Unknown}; }
0042     static ExceptionInfo createNonThrowing() { return {State::Throwing}; }
0043 
0044     /// By default the exception situation is unknown and must be
0045     /// clarified step-wise.
0046     ExceptionInfo() : Behaviour(State::NotThrowing), ContainsUnknown(false) {}
0047     ExceptionInfo(State S)
0048         : Behaviour(S), ContainsUnknown(S == State::Unknown) {}
0049 
0050     ExceptionInfo(const ExceptionInfo &) = default;
0051     ExceptionInfo &operator=(const ExceptionInfo &) = default;
0052     ExceptionInfo(ExceptionInfo &&) = default;
0053     ExceptionInfo &operator=(ExceptionInfo &&) = default;
0054 
0055     State getBehaviour() const { return Behaviour; }
0056 
0057     /// Register a single exception type as recognized potential exception to be
0058     /// thrown.
0059     void registerException(const Type *ExceptionType);
0060 
0061     /// Registers a `SmallVector` of exception types as recognized potential
0062     /// exceptions to be thrown.
0063     void registerExceptions(const Throwables &Exceptions);
0064 
0065     /// Updates the local state according to the other state. That means if
0066     /// for example a function contains multiple statements the 'ExceptionInfo'
0067     /// for the final function is the merged result of each statement.
0068     /// If one of these statements throws the whole function throws and if one
0069     /// part is unknown and the rest is non-throwing the result will be
0070     /// unknown.
0071     ExceptionInfo &merge(const ExceptionInfo &Other);
0072 
0073     /// This method is useful in case 'catch' clauses are analyzed as it is
0074     /// possible to catch multiple exception types by one 'catch' if they
0075     /// are a subclass of the 'catch'ed exception type.
0076     /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
0077     bool filterByCatch(const Type *HandlerTy, const ASTContext &Context);
0078 
0079     /// Filter the set of thrown exception type against a set of ignored
0080     /// types that shall not be considered in the exception analysis.
0081     /// This includes explicit `std::bad_alloc` ignoring as separate option.
0082     ExceptionInfo &
0083     filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes,
0084                             bool IgnoreBadAlloc);
0085 
0086     /// Clear the state to 'NonThrowing' to make the corresponding entity
0087     /// neutral.
0088     void clear();
0089 
0090     /// References the set of known exception types that can escape from the
0091     /// corresponding entity.
0092     const Throwables &getExceptionTypes() const { return ThrownExceptions; }
0093 
0094     /// Signal if the there is any 'Unknown' element within the scope of
0095     /// the related entity. This might be relevant if the entity is 'Throwing'
0096     /// and to ensure that no other exception then 'getExceptionTypes' can
0097     /// occur. If there is an 'Unknown' element this can not be guaranteed.
0098     bool containsUnknownElements() const { return ContainsUnknown; }
0099 
0100   private:
0101     /// Recalculate the 'Behaviour' for example after filtering.
0102     void reevaluateBehaviour();
0103 
0104     /// Keep track if the entity related to this 'ExceptionInfo' can in
0105     /// principle throw, if it's unknown or if it won't throw.
0106     State Behaviour;
0107 
0108     /// Keep track if the entity contains any unknown elements to keep track
0109     /// of the certainty of decisions and/or correct 'Behaviour' transition
0110     /// after filtering.
0111     bool ContainsUnknown;
0112 
0113     /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or
0114     /// 'Unknown'.
0115     Throwables ThrownExceptions;
0116   };
0117 
0118   ExceptionAnalyzer() = default;
0119 
0120   void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; }
0121   void ignoreExceptions(llvm::StringSet<> ExceptionNames) {
0122     IgnoredExceptions = std::move(ExceptionNames);
0123   }
0124 
0125   ExceptionInfo analyze(const FunctionDecl *Func);
0126   ExceptionInfo analyze(const Stmt *Stmt);
0127 
0128 private:
0129   ExceptionInfo
0130   throwsException(const FunctionDecl *Func,
0131                   const ExceptionInfo::Throwables &Caught,
0132                   llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
0133   ExceptionInfo
0134   throwsException(const Stmt *St, const ExceptionInfo::Throwables &Caught,
0135                   llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
0136 
0137   ExceptionInfo analyzeImpl(const FunctionDecl *Func);
0138   ExceptionInfo analyzeImpl(const Stmt *Stmt);
0139 
0140   template <typename T> ExceptionInfo analyzeDispatch(const T *Node);
0141 
0142   bool IgnoreBadAlloc = true;
0143   llvm::StringSet<> IgnoredExceptions;
0144   llvm::DenseMap<const FunctionDecl *, ExceptionInfo> FunctionCache{32U};
0145 };
0146 
0147 } // namespace clang::tidy::utils
0148 
0149 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H