forked from OSchip/llvm-project
157 lines
6.2 KiB
C++
157 lines
6.2 KiB
C++
//===--- 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
|