1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
//===--- 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 {
Throwing, ///< The function can definitely throw given an AST.
NotThrowing, ///< This function can not throw, given an AST.
Unknown, ///< 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, const ASTContext &Context);
/// 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,
const ExceptionInfo::Throwables &Caught,
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;
llvm::DenseMap<const FunctionDecl *, ExceptionInfo> FunctionCache{32u};
};
} // namespace clang::tidy::utils
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
|