diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp b/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp index 56df21617e5a32..c0c05b0efdbbac 100644 --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp @@ -53,11 +53,11 @@ isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin, } llvm::DenseMap -test::getAnnotationLinesAndContent(const 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()) diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h index a23b0ec0709a32..285c0c991d2316 100644 --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -96,6 +96,11 @@ template struct AnalysisInputs { /// Optional fields can be set with methods of the form `withFieldName(...)`. AnalysisInputs && + withSetupTest(std::function Arg) && { + SetupTest = std::move(Arg); + return std::move(*this); + } + AnalysisInputs && withPostVisitCFG(std::function Arg) && { @@ -120,6 +125,11 @@ template struct AnalysisInputs { /// takes as argument the AST generated from the code being analyzed and the /// initial state from which the analysis starts with. std::function MakeAnalysis; + /// Optional. 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; /// Optional. If provided, this function is applied on each CFG element after /// the analysis has been run. std::function> 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(const 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(const 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`. @@ -200,9 +166,9 @@ getAnnotationStates(const AnalysisOutputs &AO) { /// /// `VerifyResults` must be provided. template -llvm::Error checkDataflow( - AnalysisInputs AI, - std::function VerifyResults) { +llvm::Error +checkDataflow(AnalysisInputs AI, + std::function VerifyResults) { // Build AST context from code. llvm::Annotations AnnotatedCode(AI.Code); auto Unit = tooling::buildASTFromCodeWithArgs( @@ -236,7 +202,7 @@ llvm::Error checkDataflow( 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); @@ -251,19 +217,26 @@ llvm::Error checkDataflow( }; } - // 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) { + if (auto Error = AI.SetupTest(AO)) + 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 @@ -285,11 +258,10 @@ checkDataflow(AnalysisInputs AI, const AnalysisOutputs &)> VerifyResults) { return checkDataflow( - std::move(AI), - [&VerifyResults](const AnalysisOutputs &AO) -> llvm::Error { - auto AnnotationLinesAndContent = getAnnotationLinesAndContent(AO); + std::move(AI), [&VerifyResults](const AnalysisOutputs &AO) { + auto AnnotationLinesAndContent = + buildLineToAnnotationMapping(AO.ASTCtx.getSourceManager(), AO.Code); VerifyResults(AnnotationLinesAndContent, AO); - return llvm::Error::success(); }); } @@ -319,16 +291,52 @@ llvm::Error checkDataflow( std::string, DataflowAnalysisState>>, const AnalysisOutputs &)> VerifyResults) { + // Compute mapping from nodes of annotated statements to the content in the + // annotation. + llvm::DenseMap StmtToAnnotations; + auto SetupTest = [&StmtToAnnotations, + PrevSetupTest = std::move(AI.SetupTest)]( + AnalysisOutputs &AO) -> llvm::Error { + auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping( + cast(AO.InitEnv.getDeclCtx()), AO.Code); + if (!MaybeStmtToAnnotations) { + return MaybeStmtToAnnotations.takeError(); + } + StmtToAnnotations = std::move(*MaybeStmtToAnnotations); + return PrevSetupTest ? PrevSetupTest(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 = [&StmtToAnnotations, &AnnotationStates, + PrevPostVisitCFG = std::move(AI.PostVisitCFG)]( + ASTContext &Ctx, const CFGElement &Elt, + const TypeErasedDataflowAnalysisState &State) { + if (PrevPostVisitCFG) { + PrevPostVisitCFG(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](const AnalysisOutputs &AO) -> llvm::Error { - auto MaybeAnnotationStates = getAnnotationStates(AO); - if (!MaybeAnnotationStates) { - return MaybeAnnotationStates.takeError(); - } - auto &AnnotationStates = *MaybeAnnotationStates; + std::move(AI) + .withSetupTest(std::move(SetupTest)) + .withPostVisitCFG(std::move(PostVisitCFG)), + [&VerifyResults, &AnnotationStates](const AnalysisOutputs &AO) { VerifyResults(AnnotationStates, AO); - return llvm::Error::success(); }); }