summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
diff options
context:
space:
mode:
authorJonas Toth <jonas.toth@gmail.com>2019-02-20 21:04:36 +0000
committerJonas Toth <jonas.toth@gmail.com>2019-02-20 21:04:36 +0000
commit32d5b252b928105216a9b9fac7862fc1268de977 (patch)
tree1ebd912450f41a587bef0e548e1fff03224876cc /clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
parent79b5c3842b684f873d1ffad502336e973616ea51 (diff)
downloadbcm5719-llvm-32d5b252b928105216a9b9fac7862fc1268de977.tar.gz
bcm5719-llvm-32d5b252b928105216a9b9fac7862fc1268de977.zip
[clang-tidy] refactor ExceptionAnalyzer further to give ternary answer
Summary: The analsis on the throwing behvaiour on functions and statements gave only a binary answer whether an exception could occur and if yes which types are thrown. This refactoring allows keeping track if there is a unknown factor, because the code calls to some functions with unavailable source code with no `noexcept` information. This 'potential Unknown' information is propagated properly and can be queried separately. Reviewers: lebedev.ri, aaron.ballman, baloghadamsoftware, alexfh Reviewed By: lebedev.ri, baloghadamsoftware Subscribers: xazax.hun, rnkovacs, a.sidorin, Szelethus, donat.nagy, dkrupp, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D57883 llvm-svn: 354517
Diffstat (limited to 'clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h')
-rw-r--r--clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h127
1 files changed, 115 insertions, 12 deletions
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
index 327da30020e..53a6aebdc91 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -10,6 +10,7 @@
#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"
@@ -17,29 +18,131 @@ 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.
+/// 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.
+ enum State Behaviour : 2;
+
+ /// 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 : 1;
+
+ /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' or
+ /// 'Unknown'.
+ Throwables ThrownExceptions;
+ };
+ static_assert(sizeof(ExceptionInfo) <= 64u,
+ "size of exceptioninfo shall be at max one cache line");
+
ExceptionAnalyzer() = default;
- bool throwsException(const FunctionDecl *Func);
+ void ignoreBadAlloc(bool ShallIgnore) { IgnoreBadAlloc = ShallIgnore; }
void ignoreExceptions(llvm::StringSet<> ExceptionNames) {
IgnoredExceptions = std::move(ExceptionNames);
}
-private:
- using TypeVec = llvm::SmallVector<const Type *, 8>;
+ ExceptionInfo analyze(const FunctionDecl *Func);
- TypeVec throwsException(const FunctionDecl *Func,
- llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
- TypeVec throwsException(const Stmt *St, const TypeVec &Caught,
- llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
- bool isIgnoredExceptionType(const Type *Exception);
+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);
+ bool IgnoreBadAlloc = true;
llvm::StringSet<> IgnoredExceptions;
- llvm::DenseMap<const FunctionDecl *, bool> FunctionCache;
+ std::map<const FunctionDecl *, ExceptionInfo> FunctionCache;
};
} // namespace utils
} // namespace tidy
OpenPOWER on IntegriCloud