[clang] 80d9ae9 - [clang][dataflow] Fully support Environment construction for Stmt analysis. (#91616)
via cfe-commits
cfe-commits at lists.llvm.org
Wed May 15 13:11:14 PDT 2024
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;
More information about the cfe-commits
mailing list