diff options
author | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
---|---|---|
committer | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
commit | 46054fed6aeeabea27b9ba4a3ef81ab5ff9b9645 (patch) | |
tree | d12279f80b5729d0324f066002c838baa736fbd2 /clang-tidy/utils/ExceptionAnalyzer.h | |
parent | 5026a9a16d10a2edf09be54c7225f49b5789c69e (diff) | |
parent | 0eb1ac6d1df856f065717226ef34d00679a211fe (diff) |
Creating branches/google/stable and tags/google/stable/2019-05-14 from r360103upstream/stable
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/branches/google/stable@360714 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-tidy/utils/ExceptionAnalyzer.h')
-rw-r--r-- | clang-tidy/utils/ExceptionAnalyzer.h | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/clang-tidy/utils/ExceptionAnalyzer.h b/clang-tidy/utils/ExceptionAnalyzer.h new file mode 100644 index 00000000..2caa1aad --- /dev/null +++ b/clang-tidy/utils/ExceptionAnalyzer.h @@ -0,0 +1,156 @@ +//===--- 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 definitly 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 |