Author: Samira Bazuzi Date: 2024-05-15T16:11:11-04:00 New Revision: 80d9ae9cbf692a73404995a88665af7166c7e8ad
URL: https://github.com/llvm/llvm-project/commit/80d9ae9cbf692a73404995a88665af7166c7e8ad DIFF: https://github.com/llvm/llvm-project/commit/80d9ae9cbf692a73404995a88665af7166c7e8ad.diff LOG: [clang][dataflow] Fully support Environment construction for Stmt analysis. (#91616) Assume in fewer places that the analysis is of a `FunctionDecl`, and initialize the `Environment` properly for `Stmt`s. Moves constructors for `Environment` to header to make it more obvious that there are only minor differences between them and very little initialization in the constructors. Tested with check-clang-tooling. Added: Modified: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.h clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index cdf89c7def2c9..097ff2bdfe7ad 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Analysis/FlowSensitive/ASTOps.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Formula.h" @@ -30,9 +31,11 @@ #include "llvm/ADT/MapVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include <cassert> #include <memory> #include <type_traits> #include <utility> +#include <vector> namespace clang { namespace dataflow { @@ -155,7 +158,28 @@ class Environment { /// Creates an environment that uses `DACtx` to store objects that encompass /// the state of a program. - explicit Environment(DataflowAnalysisContext &DACtx); + explicit Environment(DataflowAnalysisContext &DACtx) + : DACtx(&DACtx), + FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} + + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program, with `S` as the statement to analyze. + Environment(DataflowAnalysisContext &DACtx, Stmt &S) : Environment(DACtx) { + InitialTargetStmt = &S; + } + + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program, with `FD` as the function to analyze. + /// + /// Requirements: + /// + /// The function must have a body, i.e. + /// `FunctionDecl::doesThisDecalarationHaveABody()` must be true. + Environment(DataflowAnalysisContext &DACtx, const FunctionDecl &FD) + : Environment(DACtx, *FD.getBody()) { + assert(FD.doesThisDeclarationHaveABody()); + InitialTargetFunc = &FD; + } // Copy-constructor is private, Environments should not be copied. See fork(). Environment &operator=(const Environment &Other) = delete; @@ -163,24 +187,11 @@ class Environment { Environment(Environment &&Other) = default; Environment &operator=(Environment &&Other) = default; - /// Creates an environment that uses `DACtx` to store objects that encompass - /// the state of a program. - /// - /// If `DeclCtx` is a function, initializes the environment with symbolic - /// representations of the function parameters. - /// - /// If `DeclCtx` is a non-static member function, initializes the environment - /// with a symbolic representation of the `this` pointee. - Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); - /// Assigns storage locations and values to all parameters, captures, global - /// variables, fields and functions referenced in the function currently being - /// analyzed. - /// - /// Requirements: + /// variables, fields and functions referenced in the `Stmt` or `FunctionDecl` + /// passed to the constructor. /// - /// The function must have a body, i.e. - /// `FunctionDecl::doesThisDecalarationHaveABody()` must be true. + /// If no `Stmt` or `FunctionDecl` was supplied, this function does nothing. void initialize(); /// Returns a new environment that is a copy of this one. @@ -193,7 +204,7 @@ class Environment { /// forked flow condition references the original). Environment fork() const; - /// Creates and returns an environment to use for an inline analysis of the + /// Creates and returns an environment to use for an inline analysis of the /// callee. Uses the storage location from each argument in the `Call` as the /// storage location for the corresponding parameter in the callee. /// @@ -365,46 +376,51 @@ class Environment { RecordStorageLocation & getResultObjectLocation(const Expr &RecordPRValue) const; - /// Returns the return value of the current function. This can be null if: + /// Returns the return value of the function currently being analyzed. + /// This can be null if: /// - The function has a void return type /// - No return value could be determined for the function, for example /// because it calls a function without a body. /// /// Requirements: - /// The current function must have a non-reference return type. + /// The current analysis target must be a function and must have a + /// non-reference return type. Value *getReturnValue() const { assert(getCurrentFunc() != nullptr && !getCurrentFunc()->getReturnType()->isReferenceType()); return ReturnVal; } - /// Returns the storage location for the reference returned by the current - /// function. This can be null if function doesn't return a single consistent - /// reference. + /// Returns the storage location for the reference returned by the function + /// currently being analyzed. This can be null if the function doesn't return + /// a single consistent reference. /// /// Requirements: - /// The current function must have a reference return type. + /// The current analysis target must be a function and must have a reference + /// return type. StorageLocation *getReturnStorageLocation() const { assert(getCurrentFunc() != nullptr && getCurrentFunc()->getReturnType()->isReferenceType()); return ReturnLoc; } - /// Sets the return value of the current function. + /// Sets the return value of the function currently being analyzed. /// /// Requirements: - /// The current function must have a non-reference return type. + /// The current analysis target must be a function and must have a + /// non-reference return type. void setReturnValue(Value *Val) { assert(getCurrentFunc() != nullptr && !getCurrentFunc()->getReturnType()->isReferenceType()); ReturnVal = Val; } - /// Sets the storage location for the reference returned by the current - /// function. + /// Sets the storage location for the reference returned by the function + /// currently being analyzed. /// /// Requirements: - /// The current function must have a reference return type. + /// The current analysis target must be a function and must have a reference + /// return type. void setReturnStorageLocation(StorageLocation *Loc) { assert(getCurrentFunc() != nullptr && getCurrentFunc()->getReturnType()->isReferenceType()); @@ -641,23 +657,21 @@ class Environment { /// (or the flow condition is overly constraining) or if the solver times out. bool allows(const Formula &) const; - /// Returns the `DeclContext` of the block being analysed, if any. Otherwise, - /// returns null. - const DeclContext *getDeclCtx() const { return CallStack.back(); } - /// Returns the function currently being analyzed, or null if the code being /// analyzed isn't part of a function. const FunctionDecl *getCurrentFunc() const { - return dyn_cast<FunctionDecl>(getDeclCtx()); + return CallStack.empty() ? InitialTargetFunc : CallStack.back(); } - /// Returns the size of the call stack. + /// Returns the size of the call stack, not counting the initial analysis + /// target. size_t callStackSize() const { return CallStack.size(); } /// Returns whether this `Environment` can be extended to analyze the given - /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a - /// given `MaxDepth`. - bool canDescend(unsigned MaxDepth, const DeclContext *Callee) const; + /// `Callee` (i.e. if `pushCall` can be used). + /// Recursion is not allowed. `MaxDepth` is the maximum size of the call stack + /// (i.e. the maximum value that `callStackSize()` may assume after the call). + bool canDescend(unsigned MaxDepth, const FunctionDecl *Callee) const; /// Returns the `DataflowAnalysisContext` used by the environment. DataflowAnalysisContext &getDataflowAnalysisContext() const { return *DACtx; } @@ -719,8 +733,8 @@ class Environment { ArrayRef<const Expr *> Args); /// Assigns storage locations and values to all global variables, fields - /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body. - void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl); + /// and functions in `Referenced`. + void initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced); static PrValueToResultObject buildResultObjectMap(DataflowAnalysisContext *DACtx, @@ -728,6 +742,11 @@ class Environment { RecordStorageLocation *ThisPointeeLoc, RecordStorageLocation *LocForRecordReturnVal); + static PrValueToResultObject + buildResultObjectMap(DataflowAnalysisContext *DACtx, Stmt *S, + RecordStorageLocation *ThisPointeeLoc, + RecordStorageLocation *LocForRecordReturnVal); + // `DACtx` is not null and not owned by this object. DataflowAnalysisContext *DACtx; @@ -736,11 +755,20 @@ class Environment { // shared between environments in the same call. // https://github.com/llvm/llvm-project/issues/59005 - // `DeclContext` of the block being analysed if provided. - std::vector<const DeclContext *> CallStack; + // The stack of functions called from the initial analysis target. + std::vector<const FunctionDecl *> CallStack; + + // Initial function to analyze, if a function was passed to the constructor. + // Null otherwise. + const FunctionDecl *InitialTargetFunc = nullptr; + // Top-level statement of the initial analysis target. + // If a function was passed to the constructor, this is its body. + // If a statement was passed to the constructor, this is that statement. + // Null if no analysis target was passed to the constructor. + Stmt *InitialTargetStmt = nullptr; // Maps from prvalues of record type to their result objects. Shared between - // all environments for the same function. + // all environments for the same analysis target. // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr` // here, though the cost is acceptable: The overhead of a `shared_ptr` is // incurred when it is copied, and this happens only relatively rarely (when @@ -749,7 +777,7 @@ class Environment { std::shared_ptr<PrValueToResultObject> ResultObjectMap; // The following three member variables handle various diff erent types of - // return values. + // return values when the current analysis target is a function. // - If the return type is not a reference and not a record: Value returned // by the function. Value *ReturnVal = nullptr; @@ -762,7 +790,7 @@ class Environment { RecordStorageLocation *LocForRecordReturnVal = nullptr; // The storage location of the `this` pointee. Should only be null if the - // function being analyzed is only a function and not a method. + // analysis target is not a method. RecordStorageLocation *ThisPointeeLoc = nullptr; // Maps from declarations and glvalue expression to storage locations that are diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index cb6c8b2ef1072..338a85525b384 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -16,17 +16,22 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/Analysis/FlowSensitive/ASTOps.h" +#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/ErrorHandling.h" +#include <algorithm> #include <cassert> +#include <memory> #include <utility> #define DEBUG_TYPE "dataflow" @@ -290,15 +295,14 @@ widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap, namespace { // Visitor that builds a map from record prvalues to result objects. -// This traverses the body of the function to be analyzed; for each result -// object that it encounters, it propagates the storage location of the result -// object to all record prvalues that can initialize it. +// For each result object that it encounters, it propagates the storage location +// of the result object to all record prvalues that can initialize it. class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> { public: // `ResultObjectMap` will be filled with a map from record prvalues to result - // object. If the function being analyzed returns a record by value, - // `LocForRecordReturnVal` is the location to which this record should be - // written; otherwise, it is null. + // object. If this visitor will traverse a function that returns a record by + // value, `LocForRecordReturnVal` is the location to which this record should + // be written; otherwise, it is null. explicit ResultObjectVisitor( llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap, RecordStorageLocation *LocForRecordReturnVal, @@ -514,39 +518,31 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> { } // namespace -Environment::Environment(DataflowAnalysisContext &DACtx) - : DACtx(&DACtx), - FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} - -Environment::Environment(DataflowAnalysisContext &DACtx, - const DeclContext &DeclCtx) - : Environment(DACtx) { - CallStack.push_back(&DeclCtx); -} - void Environment::initialize() { - const DeclContext *DeclCtx = getDeclCtx(); - if (DeclCtx == nullptr) + if (InitialTargetStmt == nullptr) return; - const auto *FuncDecl = dyn_cast<FunctionDecl>(DeclCtx); - if (FuncDecl == nullptr) + if (InitialTargetFunc == nullptr) { + initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetStmt)); + ResultObjectMap = + std::make_shared<PrValueToResultObject>(buildResultObjectMap( + DACtx, InitialTargetStmt, getThisPointeeStorageLocation(), + /*LocForRecordReturnValue=*/nullptr)); return; + } - assert(FuncDecl->doesThisDeclarationHaveABody()); - - initFieldsGlobalsAndFuncs(FuncDecl); + initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetFunc)); - for (const auto *ParamDecl : FuncDecl->parameters()) { + for (const auto *ParamDecl : InitialTargetFunc->parameters()) { assert(ParamDecl != nullptr); setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); } - if (FuncDecl->getReturnType()->isRecordType()) + if (InitialTargetFunc->getReturnType()->isRecordType()) LocForRecordReturnVal = &cast<RecordStorageLocation>( - createStorageLocation(FuncDecl->getReturnType())); + createStorageLocation(InitialTargetFunc->getReturnType())); - if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(DeclCtx)) { + if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(InitialTargetFunc)) { auto *Parent = MethodDecl->getParent(); assert(Parent != nullptr); @@ -558,7 +554,7 @@ void Environment::initialize() { setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); } else if (Capture.capturesThis()) { const auto *SurroundingMethodDecl = - cast<CXXMethodDecl>(DeclCtx->getNonClosureAncestor()); + cast<CXXMethodDecl>(InitialTargetFunc->getNonClosureAncestor()); QualType ThisPointeeType = SurroundingMethodDecl->getFunctionObjectParameterType(); setThisPointeeStorageLocation( @@ -580,18 +576,16 @@ void Environment::initialize() { // We do this below the handling of `CXXMethodDecl` above so that we can // be sure that the storage location for `this` has been set. - ResultObjectMap = std::make_shared<PrValueToResultObject>( - buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(), - LocForRecordReturnVal)); + ResultObjectMap = + std::make_shared<PrValueToResultObject>(buildResultObjectMap( + DACtx, InitialTargetFunc, getThisPointeeStorageLocation(), + LocForRecordReturnVal)); } -// FIXME: Add support for resetting globals after function calls to enable -// the implementation of sound analyses. -void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { - assert(FuncDecl->doesThisDeclarationHaveABody()); - - ReferencedDecls Referenced = getReferencedDecls(*FuncDecl); +// FIXME: Add support for resetting globals after function calls to enable the +// implementation of sound analyses. +void Environment::initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced) { // These have to be added before the lines that follow to ensure that // `create*` work correctly for structs. DACtx->addModeledFields(Referenced.Fields); @@ -602,9 +596,9 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { // We don't run transfer functions on the initializers of global variables, // so they won't be associated with a value or storage location. We - // therefore intentionally don't pass an initializer to `createObject()`; - // in particular, this ensures that `createObject()` will initialize the - // fields of record-type variables with values. + // therefore intentionally don't pass an initializer to `createObject()`; in + // particular, this ensures that `createObject()` will initialize the fields + // of record-type variables with values. setStorageLocation(*D, createObject(*D, nullptr)); } @@ -623,8 +617,8 @@ Environment Environment::fork() const { } bool Environment::canDescend(unsigned MaxDepth, - const DeclContext *Callee) const { - return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee); + const FunctionDecl *Callee) const { + return CallStack.size() < MaxDepth && !llvm::is_contained(CallStack, Callee); } Environment Environment::pushCall(const CallExpr *Call) const { @@ -671,7 +665,7 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl, CallStack.push_back(FuncDecl); - initFieldsGlobalsAndFuncs(FuncDecl); + initFieldsGlobalsAndFuncs(getReferencedDecls(*FuncDecl)); const auto *ParamIt = FuncDecl->param_begin(); @@ -755,6 +749,8 @@ LatticeEffect Environment::widen(const Environment &PrevEnv, assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc); assert(CallStack == PrevEnv.CallStack); assert(ResultObjectMap == PrevEnv.ResultObjectMap); + assert(InitialTargetFunc == PrevEnv.InitialTargetFunc); + assert(InitialTargetStmt == PrevEnv.InitialTargetStmt); auto Effect = LatticeEffect::Unchanged; @@ -790,6 +786,8 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB, assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc); assert(EnvA.CallStack == EnvB.CallStack); assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap); + assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc); + assert(EnvA.InitialTargetStmt == EnvB.InitialTargetStmt); Environment JoinedEnv(*EnvA.DACtx); @@ -797,14 +795,13 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB, JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap; JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal; JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc; + JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc; + JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt; - if (EnvA.CallStack.empty()) { + const FunctionDecl *Func = EnvA.getCurrentFunc(); + if (!Func) { JoinedEnv.ReturnVal = nullptr; } else { - // FIXME: Make `CallStack` a vector of `FunctionDecl` so we don't need this - // cast. - auto *Func = dyn_cast<FunctionDecl>(EnvA.CallStack.back()); - assert(Func != nullptr); JoinedEnv.ReturnVal = joinValues(Func->getReturnType(), EnvA.ReturnVal, EnvA, EnvB.ReturnVal, EnvB, JoinedEnv, Model); @@ -1229,16 +1226,26 @@ Environment::PrValueToResultObject Environment::buildResultObjectMap( RecordStorageLocation *LocForRecordReturnVal) { assert(FuncDecl->doesThisDeclarationHaveABody()); - PrValueToResultObject Map; + PrValueToResultObject Map = buildResultObjectMap( + DACtx, FuncDecl->getBody(), ThisPointeeLoc, LocForRecordReturnVal); ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx); if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl)) Visitor.TraverseConstructorInits(Ctor, ThisPointeeLoc); - Visitor.TraverseStmt(FuncDecl->getBody()); return Map; } +Environment::PrValueToResultObject Environment::buildResultObjectMap( + DataflowAnalysisContext *DACtx, Stmt *S, + RecordStorageLocation *ThisPointeeLoc, + RecordStorageLocation *LocForRecordReturnVal) { + PrValueToResultObject Map; + ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx); + Visitor.TraverseStmt(S); + return Map; +} + RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env) { Expr *ImplicitObject = MCE.getImplicitObjectArgument(); diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 12eff4dd4b781..675b42550f177 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -476,7 +476,7 @@ runTypeErasedDataflowAnalysis( PrettyStackTraceAnalysis CrashInfo(ACFG, "runTypeErasedDataflowAnalysis"); std::optional<Environment> MaybeStartingEnv; - if (InitEnv.callStackSize() == 1) { + if (InitEnv.callStackSize() == 0) { MaybeStartingEnv = InitEnv.fork(); MaybeStartingEnv->initialize(); } diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp index 4195648161246..bd710a00c47ce 100644 --- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp @@ -9,6 +9,8 @@ #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "TestingSupport.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" @@ -403,4 +405,35 @@ TEST_F(EnvironmentTest, Contains(Member)); } +TEST_F(EnvironmentTest, Stmt) { + using namespace ast_matchers; + + std::string Code = R"cc( + struct S { int i; }; + void foo() { + S AnS = S{1}; + } + )cc"; + auto Unit = + tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); + auto &Context = Unit->getASTContext(); + + ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); + + auto *DeclStatement = const_cast<DeclStmt *>(selectFirst<DeclStmt>( + "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"), + Context))); + ASSERT_THAT(DeclStatement, NotNull()); + auto *Init = (cast<VarDecl>(*DeclStatement->decl_begin()))->getInit(); + ASSERT_THAT(Init, NotNull()); + + // Verify that we can retrieve the result object location for the initializer + // expression when we analyze the DeclStmt for `AnS`. + Environment Env(DAContext, *DeclStatement); + // Don't crash when initializing. + Env.initialize(); + // And don't crash when retrieving the result object location. + Env.getResultObjectLocation(*Init); +} + } // namespace diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h index 3b0e05ed72220..7348f8b1740d0 100644 --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -355,8 +355,8 @@ checkDataflow(AnalysisInputs<AnalysisT> AI, auto SetupTest = [&StmtToAnnotations, PrevSetupTest = std::move(AI.SetupTest)]( AnalysisOutputs &AO) -> llvm::Error { - auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping( - cast<FunctionDecl>(AO.InitEnv.getDeclCtx()), AO.Code); + auto MaybeStmtToAnnotations = + buildStatementToAnnotationMapping(AO.InitEnv.getCurrentFunc(), AO.Code); if (!MaybeStmtToAnnotations) { return MaybeStmtToAnnotations.takeError(); } diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp index b0b579d2bc19e..1a52b82d65665 100644 --- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -146,6 +146,38 @@ TEST_F(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) { " (Lifetime ends)\n"))); } +TEST_F(DataflowAnalysisTest, CanAnalyzeStmt) { + std::string Code = R"cc( + struct S { bool b; }; + void foo() { + S AnS = S{true}; + } + )cc"; + AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); + const auto &DeclStatement = + matchNode<DeclStmt>(declStmt(hasSingleDecl(varDecl(hasName("AnS"))))); + const auto &Func = matchNode<FunctionDecl>(functionDecl(hasName("foo"))); + + ACFG = std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build( + Func, const_cast<DeclStmt &>(DeclStatement), AST->getASTContext()))); + + NoopAnalysis Analysis = NoopAnalysis(AST->getASTContext()); + DACtx = std::make_unique<DataflowAnalysisContext>( + std::make_unique<WatchedLiteralsSolver>()); + Environment Env(*DACtx, const_cast<DeclStmt &>(DeclStatement)); + + llvm::Expected<std::vector<std::optional<DataflowAnalysisState<NoopLattice>>>> + Results = runDataflowAnalysis(*ACFG, Analysis, Env); + + ASSERT_THAT_ERROR(Results.takeError(), llvm::Succeeded()); + const Environment &ExitBlockEnv = Results->front()->Env; + BoolValue *BoolFieldValue = cast<BoolValue>( + getFieldValue(ExitBlockEnv.get<RecordStorageLocation>( + *cast<VarDecl>((*DeclStatement.decl_begin()))), + "b", AST->getASTContext(), ExitBlockEnv)); + EXPECT_TRUE(Env.proves(BoolFieldValue->formula())); +} + // Tests for the statement-to-block map. using StmtToBlockTest = DataflowAnalysisTest; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits