diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -56,28 +56,6 @@ namespace test { -/// Arguments for building the dataflow analysis. -template struct AnalysisInputs { - /// Input code that is analyzed. - llvm::StringRef Code; - /// The body of the function which matches this matcher is analyzed. - ast_matchers::internal::Matcher TargetFuncMatcher; - /// The analysis to be run is constructed with this function that takes as - /// argument the AST generated from the code being analyzed and the - /// initial state from which the analysis starts with. - std::function MakeAnalysis; - /// If provided, this function is applied on each CFG element after the - /// analysis has been run. - std::function - PostVisitCFG = nullptr; - - /// Options for building the AST context. - ArrayRef ASTBuildArgs = {}; - /// Options for building the AST context. - const tooling::FileContentMappings &ASTBuildVirtualMappedFiles = {}; -}; - /// Contains data structures required and produced by a dataflow analysis run. struct AnalysisOutputs { /// Input code that is analyzed. Points within the code may be marked with @@ -106,60 +84,43 @@ llvm::ArrayRef> BlockStates; }; +/// Arguments for building the dataflow analysis. +template struct AnalysisInputs { + /// Input code that is analyzed. + llvm::StringRef Code; + /// The body of the function which matches this matcher is analyzed. + ast_matchers::internal::Matcher TargetFuncMatcher; + /// The analysis to be run is constructed with this function that takes as + /// argument the AST generated from the code being analyzed and the + /// initial state from which the analysis starts with. + std::function MakeAnalysis; + /// If provided, this function is applied on each CFG element after the + /// analysis has been run. + std::function + PostVisitCFG = nullptr; + /// If provided, this function is executed immediately before running the + /// dataflow analysis to allow for additional setup. All fields in the + /// `AnalysisOutputs` argument will be initialized except for the + /// `BlockStates` field which is only computed later during the analysis. + std::function SetupTest = nullptr; + + /// Options for building the AST context. + ArrayRef ASTBuildArgs = {}; + /// Options for building the AST context. + const tooling::FileContentMappings &ASTBuildVirtualMappedFiles = {}; +}; + /// Returns assertions based on annotations that are present after statements in /// `AnnotatedCode`. llvm::Expected> buildStatementToAnnotationMapping(const FunctionDecl *Func, llvm::Annotations AnnotatedCode); -/// Returns line numbers and content of the annotations in `AO.Code`. +/// Returns line numbers and content of the annotations in `AnnotatedCode`. llvm::DenseMap -getAnnotationLinesAndContent(AnalysisOutputs &AO); - -// FIXME: Return a string map instead of a vector of pairs. -// -/// Returns the analysis states at each annotated statement in `AO.Code`. -template -llvm::Expected>>> -getAnnotationStates(AnalysisOutputs &AO) { - using StateT = DataflowAnalysisState; - // FIXME: Extend to annotations on non-statement constructs. - // Get annotated statements. - llvm::Expected> - MaybeStmtToAnnotations = - buildStatementToAnnotationMapping(AO.Target, AO.Code); - if (!MaybeStmtToAnnotations) - return MaybeStmtToAnnotations.takeError(); - auto &StmtToAnnotations = *MaybeStmtToAnnotations; - - // Compute a map from statement annotations to the state computed - // for the program point immediately after the annotated statement. - std::vector> Results; - for (const CFGBlock *Block : AO.CFCtx.getCFG()) { - // Skip blocks that were not evaluated. - if (!AO.BlockStates[Block->getBlockID()]) - continue; - - transferBlock( - AO.CFCtx, AO.BlockStates, *Block, AO.InitEnv, AO.Analysis, - [&Results, - &StmtToAnnotations](const clang::CFGElement &Element, - const TypeErasedDataflowAnalysisState &State) { - auto Stmt = Element.getAs(); - if (!Stmt) - return; - auto It = StmtToAnnotations.find(Stmt->getStmt()); - if (It == StmtToAnnotations.end()) - return; - auto *Lattice = - llvm::any_cast(&State.Lattice.Value); - Results.emplace_back(It->second, StateT{*Lattice, State.Env}); - }); - } - - return Results; -} +buildLineToAnnotationMapping(SourceManager &SM, + llvm::Annotations AnnotatedCode); /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the /// body of the function that matches `AI.TargetFuncMatcher` in `AI.Code`. @@ -172,7 +133,7 @@ template llvm::Error checkDataflow(AnalysisInputs AI, - std::function VerifyResults) { + std::function VerifyResults) { // Build AST context from code. llvm::Annotations AnnotatedCode(AI.Code); auto Unit = tooling::buildASTFromCodeWithArgs( @@ -206,7 +167,7 @@ return MaybeCFCtx.takeError(); auto &CFCtx = *MaybeCFCtx; - // Initialize states and run dataflow analysis. + // Initialize states for running dataflow analysis. DataflowAnalysisContext DACtx(std::make_unique()); Environment InitEnv(DACtx, *Target); auto Analysis = AI.MakeAnalysis(Context, InitEnv); @@ -221,19 +182,28 @@ }; } - // If successful, the run returns a mapping from block IDs to the - // post-analysis states for the CFG blocks that have been evaluated. + // Additional test setup. + AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx, + Analysis, InitEnv, {}}; + if (AI.SetupTest) { + auto Error = AI.SetupTest(AO); + if (Error) { + return Error; + } + } + + // If successful, the dataflow analysis returns a mapping from block IDs to + // the post-analysis states for the CFG blocks that have been evaluated. llvm::Expected>> MaybeBlockStates = runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv, PostVisitCFGClosure); if (!MaybeBlockStates) return MaybeBlockStates.takeError(); - auto &BlockStates = *MaybeBlockStates; + AO.BlockStates = *MaybeBlockStates; // Verify dataflow analysis outputs. - AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx, - Analysis, InitEnv, BlockStates}; - return VerifyResults(AO); + VerifyResults(AO); + return llvm::Error::success(); } /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the @@ -251,10 +221,10 @@ AnalysisOutputs &)> VerifyResults) { return checkDataflow( - std::move(AI), [&VerifyResults](AnalysisOutputs &AO) -> llvm::Error { - auto AnnotationLinesAndContent = getAnnotationLinesAndContent(AO); + std::move(AI), [&VerifyResults](AnalysisOutputs &AO) { + auto AnnotationLinesAndContent = + buildLineToAnnotationMapping(AO.ASTCtx.getSourceManager(), AO.Code); VerifyResults(AnnotationLinesAndContent, AO); - return llvm::Error::success(); }); } @@ -280,15 +250,49 @@ std::string, DataflowAnalysisState>>, AnalysisOutputs &)> VerifyResults) { + // Compute mapping from nodes of annotated statements to the content in the + // annotation. + llvm::DenseMap StmtToAnnotations; + AI.SetupTest = [&StmtToAnnotations, SetupTest = std::move(AI.SetupTest)]( + AnalysisOutputs &AO) -> llvm::Error { + auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping( + cast(AO.InitEnv.getDeclCtx()), AO.Code); + if (!MaybeStmtToAnnotations) { + return MaybeStmtToAnnotations.takeError(); + } + StmtToAnnotations = *MaybeStmtToAnnotations; + return SetupTest ? SetupTest(AO) : llvm::Error::success(); + }; + + using StateT = DataflowAnalysisState; + + // FIXME: Use a string map instead of a vector of pairs. + // + // Save the states computed for program points immediately following annotated + // statements. The saved states are keyed by the content of the annotation. + std::vector> AnnotationStates; + auto PostVisitCFG = AI.PostVisitCFG; + AI.PostVisitCFG = [&StmtToAnnotations, &AnnotationStates, + PostVisitCFG = std::move(AI.PostVisitCFG)]( + ASTContext &Ctx, const CFGElement &Elt, + const TypeErasedDataflowAnalysisState &State) { + if (PostVisitCFG) { + PostVisitCFG(Ctx, Elt, State); + } + // FIXME: Extend retrieval of state for non statement constructs. + auto Stmt = Elt.getAs(); + if (!Stmt) + return; + auto It = StmtToAnnotations.find(Stmt->getStmt()); + if (It == StmtToAnnotations.end()) + return; + auto *Lattice = + llvm::any_cast(&State.Lattice.Value); + AnnotationStates.emplace_back(It->second, StateT{*Lattice, State.Env}); + }; return checkDataflow( - std::move(AI), [&VerifyResults](AnalysisOutputs &AO) -> llvm::Error { - auto MaybeAnnotationStates = getAnnotationStates(AO); - if (!MaybeAnnotationStates) { - return MaybeAnnotationStates.takeError(); - } - auto &AnnotationStates = *MaybeAnnotationStates; + std::move(AI), [&VerifyResults, &AnnotationStates](AnalysisOutputs &AO) { VerifyResults(AnnotationStates, AO); - return llvm::Error::success(); }); } diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp b/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp @@ -53,11 +53,11 @@ } llvm::DenseMap -test::getAnnotationLinesAndContent(AnalysisOutputs &AO) { +test::buildLineToAnnotationMapping(SourceManager &SM, + llvm::Annotations AnnotatedCode) { llvm::DenseMap LineNumberToContent; - auto Code = AO.Code.code(); - auto Annotations = AO.Code.ranges(); - auto &SM = AO.ASTCtx.getSourceManager(); + auto Code = AnnotatedCode.code(); + auto Annotations = AnnotatedCode.ranges(); for (auto &AnnotationRange : Annotations) { auto LineNumber = SM.getPresumedLineNumber(SM.getLocForStartOfFile(SM.getMainFileID())