123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- //===--- 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::tidy::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 clang::tidy::utils
- #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
|