ymandel created this revision. ymandel added reviewers: xazax.hun, mboehme. Herald added a subscriber: martong. Herald added a reviewer: NoQ. Herald added a project: All. ymandel requested review of this revision. Herald added a project: clang.
The convenience function captures running the analysis and then collecting diagnostics based on a `Diagnoser` object. This pattern is valuable to clang-tidy checks which analyze a function at a time, though it could be more generally useful for analysis clients. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D156254 Files: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp =================================================================== --- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -47,6 +47,7 @@ using namespace ast_matchers; using llvm::IsStringMapEntry; using ::testing::DescribeMatcher; +using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::NotNull; using ::testing::Test; @@ -84,6 +85,28 @@ EXPECT_TRUE(BlockStates[1].has_value()); } +// Basic test that `diagnoseFunction` calls the Diagnoser function for the +// number of elements expected. +TEST(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) { + std::string Code = R"(void target() { int x = 0; ++x; })"; + std::unique_ptr<ASTUnit> AST = + tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); + + auto *Func = selectFirst<FunctionDecl>( + "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"), + AST->getASTContext())); + ASSERT_THAT(Func, NotNull()); + + int Count = 0; + auto Diagnoser = [&Count](const CFGElement &, ASTContext &, + const TransferStateForDiagnostics<NoopLattice> &) { + return std::vector<int>({++Count}); + }; + auto Result = diagnoseFunction<NoopAnalysis, int>(*Func, AST->getASTContext(), + Diagnoser); + EXPECT_THAT_EXPECTED(Result, llvm::HasValue(ElementsAre(1, 2, 3, 4))); +} + struct NonConvergingLattice { int State; Index: clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h =================================================================== --- clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h +++ clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h @@ -24,6 +24,9 @@ class NoopAnalysis : public DataflowAnalysis<NoopAnalysis, NoopLattice> { public: + NoopAnalysis(ASTContext &Context) + : DataflowAnalysis<NoopAnalysis, NoopLattice>(Context) {} + /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead. NoopAnalysis(ASTContext &Context, bool ApplyBuiltinTransfer) : DataflowAnalysis<NoopAnalysis, NoopLattice>(Context, Index: clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h =================================================================== --- clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h +++ clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h @@ -48,6 +48,10 @@ }; /// A read-only version of TransferState. +/// +/// FIXME: this type is being used as a general (typed) view type for untyped +/// dataflow analysis state, rather than strictly for transfer-function +/// purposes. Move it (and rename it) to DataflowAnalysis.h. template <typename LatticeT> struct TransferStateForDiagnostics { TransferStateForDiagnostics(const LatticeT &Lattice, const Environment &Env) : Lattice(Lattice), Env(Env) {} Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h =================================================================== --- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -25,9 +25,12 @@ #include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" +#include "clang/Analysis/FlowSensitive/MatchSwitch.h" #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" +#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" #include "llvm/ADT/Any.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" namespace clang { @@ -238,6 +241,59 @@ return std::move(BlockStates); } +/// Runs a dataflow analysis over the given function and then runs `Diagnoser` +/// over the results. If the analysis is successful, returns a list of +/// diagnostics for `FuncDecl`. Otherwise, returns an error. Currently, errors +/// can occur (at least) because the analysis requires too many iterations over +/// the CFG or the SAT solver times out. +/// +/// The default value of `MaxSATIterations` was chosen based on the following +/// observations: +/// - Non-pathological calls to the solver typically require only a few hundred +/// iterations. +/// - This limit is still low enough to keep runtimes acceptable (on typical +/// machines) in cases where we hit the limit. +template <typename AnalysisT, typename Diagnostic, typename DiagnoserT> +llvm::Expected<std::vector<Diagnostic>> +diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, + DiagnoserT &Diagnoser, + std::int64_t MaxSATIterations = 1'000'000'000) { + using ::llvm::Expected; + + Expected<ControlFlowContext> Context = ControlFlowContext::build(FuncDecl); + if (!Context) + return Context.takeError(); + + auto Solver = std::make_unique<WatchedLiteralsSolver>(MaxSATIterations); + const WatchedLiteralsSolver *SolverView = Solver.get(); + DataflowAnalysisContext AnalysisContext(std::move(Solver)); + Environment Env(AnalysisContext, FuncDecl); + AnalysisT Analysis(ASTCtx); + std::vector<Diagnostic> Diagnostics; + auto BlockToOutputState = runTypeErasedDataflowAnalysis( + *Context, Analysis, Env, + [&ASTCtx, &Diagnoser, + &Diagnostics](const CFGElement &Elt, + const TypeErasedDataflowAnalysisState &State) mutable { + auto EltDiagnostics = + Diagnoser(Elt, ASTCtx, + TransferStateForDiagnostics<typename AnalysisT::Lattice>( + llvm::any_cast<const typename AnalysisT::Lattice &>( + State.Lattice.Value), + State.Env)); + llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); + }); + + if (!BlockToOutputState) + return BlockToOutputState.takeError(); + + if (SolverView->reachedLimit()) + return llvm::createStringError(llvm::errc::interrupted, + "SAT solver timed out"); + + return Diagnostics; +} + /// Abstract base class for dataflow "models": reusable analysis components that /// model a particular aspect of program semantics in the `Environment`. For /// example, a model may capture a type and its related functions.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits