162 lines · c
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTIONANALYZER_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTIONANALYZER_H11 12#include "clang/AST/ASTContext.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "llvm/ADT/SmallSet.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/ADT/StringSet.h"17 18namespace clang::tidy::utils {19 20/// This class analysis if a `FunctionDecl` can in principle throw an21/// exception, either directly or indirectly. It can be configured to ignore22/// custom exception types.23class ExceptionAnalyzer {24public:25 enum class State {26 Throwing, ///< The function can definitely throw given an AST.27 NotThrowing, ///< This function can not throw, given an AST.28 Unknown, ///< This can happen for extern functions without available29 ///< definition.30 };31 32 /// We use a MapVector to preserve the order of the functions in the call33 /// stack as well as have fast lookup.34 using CallStack = llvm::MapVector<const FunctionDecl *, SourceLocation>;35 36 /// Bundle the gathered information about an entity like a function regarding37 /// it's exception behaviour. The 'NonThrowing'-state can be considered as the38 /// neutral element in terms of information propagation.39 /// In the case of 'Throwing' state it is possible that 'getExceptionTypes'40 /// does not include *ALL* possible types as there is the possibility that41 /// an 'Unknown' function is called that might throw a previously unknown42 /// exception at runtime.43 class ExceptionInfo {44 public:45 /// Holds information about where an exception is thrown.46 /// First element in the call stack is analyzed function.47 struct ThrowInfo {48 SourceLocation Loc;49 CallStack Stack;50 };51 52 using Throwables = llvm::SmallDenseMap<const Type *, ThrowInfo, 2>;53 54 static ExceptionInfo createUnknown() { return {State::Unknown}; }55 static ExceptionInfo createNonThrowing() { return {State::Throwing}; }56 57 /// By default the exception situation is unknown and must be58 /// clarified step-wise.59 ExceptionInfo() : Behaviour(State::NotThrowing), ContainsUnknown(false) {}60 ExceptionInfo(State S)61 : Behaviour(S), ContainsUnknown(S == State::Unknown) {}62 63 ExceptionInfo(const ExceptionInfo &) = default;64 ExceptionInfo &operator=(const ExceptionInfo &) = default;65 ExceptionInfo(ExceptionInfo &&) = default;66 ExceptionInfo &operator=(ExceptionInfo &&) = default;67 68 State getBehaviour() const { return Behaviour; }69 70 /// Register a single exception type as recognized potential exception to be71 /// thrown.72 void registerException(const Type *ExceptionType,73 const ThrowInfo &ThrowInfo);74 75 /// Registers a `SmallVector` of exception types as recognized potential76 /// exceptions to be thrown.77 void registerExceptions(const Throwables &Exceptions);78 79 /// Updates the local state according to the other state. That means if80 /// for example a function contains multiple statements the 'ExceptionInfo'81 /// for the final function is the merged result of each statement.82 /// If one of these statements throws the whole function throws and if one83 /// part is unknown and the rest is non-throwing the result will be84 /// unknown.85 ExceptionInfo &merge(const ExceptionInfo &Other);86 87 /// This method is useful in case 'catch' clauses are analyzed as it is88 /// possible to catch multiple exception types by one 'catch' if they89 /// are a subclass of the 'catch'ed exception type.90 /// Returns filtered exceptions.91 Throwables filterByCatch(const Type *HandlerTy, const ASTContext &Context);92 93 /// Filter the set of thrown exception type against a set of ignored94 /// types that shall not be considered in the exception analysis.95 /// This includes explicit `std::bad_alloc` ignoring as separate option.96 ExceptionInfo &97 filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes,98 bool IgnoreBadAlloc);99 100 /// Clear the state to 'NonThrowing' to make the corresponding entity101 /// neutral.102 void clear();103 104 /// References the set of known exceptions that can escape from the105 /// corresponding entity.106 const Throwables &getExceptions() const { return ThrownExceptions; }107 108 /// Signal if the there is any 'Unknown' element within the scope of109 /// the related entity. This might be relevant if the entity is 'Throwing'110 /// and to ensure that no other exception then 'getExceptionTypes' can111 /// occur. If there is an 'Unknown' element this can not be guaranteed.112 bool containsUnknownElements() const { return ContainsUnknown; }113 114 private:115 /// Recalculate the 'Behaviour' for example after filtering.116 void reevaluateBehaviour();117 118 /// Keep track if the entity related to this 'ExceptionInfo' can in119 /// principle throw, if it's unknown or if it won't throw.120 State Behaviour;121 122 /// Keep track if the entity contains any unknown elements to keep track123 /// of the certainty of decisions and/or correct 'Behaviour' transition124 /// after filtering.125 bool ContainsUnknown;126 127 /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or128 /// 'Unknown'.129 Throwables ThrownExceptions;130 };131 132 ExceptionAnalyzer() = default;133 134 void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; }135 void ignoreExceptions(llvm::StringSet<> ExceptionNames) {136 IgnoredExceptions = std::move(ExceptionNames);137 }138 139 ExceptionInfo analyze(const FunctionDecl *Func);140 ExceptionInfo analyze(const Stmt *Stmt);141 142private:143 ExceptionInfo throwsException(const FunctionDecl *Func,144 const ExceptionInfo::Throwables &Caught,145 CallStack &CallStack, SourceLocation CallLoc);146 ExceptionInfo throwsException(const Stmt *St,147 const ExceptionInfo::Throwables &Caught,148 CallStack &CallStack);149 ExceptionInfo analyzeImpl(const FunctionDecl *Func);150 ExceptionInfo analyzeImpl(const Stmt *Stmt);151 152 template <typename T> ExceptionInfo analyzeDispatch(const T *Node);153 154 bool IgnoreBadAlloc = true;155 llvm::StringSet<> IgnoredExceptions;156 llvm::DenseMap<const FunctionDecl *, ExceptionInfo> FunctionCache{32U};157};158 159} // namespace clang::tidy::utils160 161#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTIONANALYZER_H162