[clang] 5e8f597 - [clang][dataflow] Only model struct fields that are used in the function being analyzed.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 5 13:47:19 PST 2023
Author: Yitzhak Mandelbaum
Date: 2023-01-05T21:46:39Z
New Revision: 5e8f597c2fedc740b71f07dfdb1ef3c2d348b193
URL: https://github.com/llvm/llvm-project/commit/5e8f597c2fedc740b71f07dfdb1ef3c2d348b193
DIFF: https://github.com/llvm/llvm-project/commit/5e8f597c2fedc740b71f07dfdb1ef3c2d348b193.diff
LOG: [clang][dataflow] Only model struct fields that are used in the function being analyzed.
Previously, the model for structs modeled all fields in a struct when
`createValue` was called for that type. This patch adds a prepass on the
function under analysis to discover the fields referenced in the scope and then
limits modeling to only those fields. This reduces wasted memory usage
(modeling unused fields) which can be important for programss that use large
structs.
Note: This patch obviates the need for https://reviews.llvm.org/D123032.
Differential Revision: https://reviews.llvm.org/D140694
Added:
Modified:
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
clang/unittests/Analysis/FlowSensitive/TestingSupport.h
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index f8ee5864482b3..98135508aabb4 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -54,14 +54,21 @@ llvm::DenseSet<const FieldDecl *> getObjectFields(QualType Type);
/// is used during dataflow analysis.
class DataflowAnalysisContext {
public:
+ // FIXME: merge with TransferOptions from Transfer.h.
+ struct Options {
+ bool EnableContextSensitiveAnalysis;
+ };
+
/// Constructs a dataflow analysis context.
///
/// Requirements:
///
/// `S` must not be null.
- DataflowAnalysisContext(std::unique_ptr<Solver> S)
+ DataflowAnalysisContext(std::unique_ptr<Solver> S,
+ Options Opts = {
+ /*EnableContextSensitiveAnalysis=*/false})
: S(std::move(S)), TrueVal(createAtomicBoolValue()),
- FalseVal(createAtomicBoolValue()) {
+ FalseVal(createAtomicBoolValue()), Options(Opts) {
assert(this->S != nullptr);
}
@@ -253,7 +260,11 @@ class DataflowAnalysisContext {
/// returns null.
const ControlFlowContext *getControlFlowContext(const FunctionDecl *F);
+ void addFieldsReferencedInScope(llvm::DenseSet<const FieldDecl *> Fields);
+
private:
+ friend class Environment;
+
struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo<QualType> {
static QualType getEmptyKey() {
// Allow a NULL `QualType` by using a
diff erent value as the empty key.
@@ -265,6 +276,10 @@ class DataflowAnalysisContext {
using DenseMapInfo::isEqual;
};
+ /// Returns the subset of fields of `Type` that are referenced in the scope of
+ /// the analysis.
+ llvm::DenseSet<const FieldDecl *> getReferencedFields(QualType Type);
+
/// Adds all constraints of the flow condition identified by `Token` and all
/// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
/// to track tokens of flow conditions that were already visited by recursive
@@ -330,6 +345,8 @@ class DataflowAnalysisContext {
AtomicBoolValue &TrueVal;
AtomicBoolValue &FalseVal;
+ Options Options;
+
// Indices that are used to avoid recreating the same composite boolean
// values.
llvm::DenseMap<std::pair<BoolValue *, BoolValue *>, ConjunctionValue *>
@@ -359,6 +376,9 @@ class DataflowAnalysisContext {
llvm::DenseMap<AtomicBoolValue *, BoolValue *> FlowConditionConstraints;
llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
+
+ // All fields referenced (statically) in the scope of the analysis.
+ llvm::DenseSet<const FieldDecl *> FieldsReferencedInScope;
};
} // namespace dataflow
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index c1653c2ab4ed2..fbc5f6d2c8891 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -452,6 +452,9 @@ class Environment {
void pushCallInternal(const FunctionDecl *FuncDecl,
ArrayRef<const Expr *> Args);
+ /// Assigns storage locations and values to all variables in `Vars`.
+ void initVars(llvm::DenseSet<const VarDecl *> Vars);
+
// `DACtx` is not null and not owned by this object.
DataflowAnalysisContext *DACtx;
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index af2f1fcbe24ee..6b7b2dc8912bf 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/Analysis/FlowSensitive/DebugSupport.h"
#include "clang/Analysis/FlowSensitive/Value.h"
+#include "llvm/ADT/SetOperations.h"
#include "llvm/Support/Debug.h"
#include <cassert>
#include <memory>
@@ -24,13 +25,33 @@
namespace clang {
namespace dataflow {
+void DataflowAnalysisContext::addFieldsReferencedInScope(
+ llvm::DenseSet<const FieldDecl *> Fields) {
+ llvm::set_union(FieldsReferencedInScope, Fields);
+}
+
+llvm::DenseSet<const FieldDecl *>
+DataflowAnalysisContext::getReferencedFields(QualType Type) {
+ llvm::DenseSet<const FieldDecl *> Fields = getObjectFields(Type);
+ llvm::set_intersect(Fields, FieldsReferencedInScope);
+ return Fields;
+}
+
StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
if (!Type.isNull() &&
(Type->isStructureOrClassType() || Type->isUnionType())) {
- // FIXME: Explore options to avoid eager initialization of fields as some of
- // them might not be needed for a particular analysis.
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
- for (const FieldDecl *Field : getObjectFields(Type))
+ // During context-sensitive analysis, a struct may be allocated in one
+ // function, but its field accessed in a function lower in the stack than
+ // the allocation. Since we only collect fields used in the function where
+ // the allocation occurs, we can't apply that filter when performing
+ // context-sensitive analysis. But, this only applies to storage locations,
+ // since fields access it not allowed to fail. In contrast, field *values*
+ // don't need this allowance, since the API allows for uninitialized fields.
+ auto Fields = Options.EnableContextSensitiveAnalysis
+ ? getObjectFields(Type)
+ : getReferencedFields(Type);
+ for (const FieldDecl *Field : Fields)
FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
return takeOwnership(
std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 1a561b31aae9a..936f10ef3824d 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -151,44 +151,62 @@ static Value &widenDistinctValues(QualType Type, Value &Prev,
}
/// Initializes a global storage value.
-static void initGlobalVar(const VarDecl &D, Environment &Env) {
- if (!D.hasGlobalStorage() ||
- Env.getStorageLocation(D, SkipPast::None) != nullptr)
- return;
-
- auto &Loc = Env.createStorageLocation(D);
- Env.setStorageLocation(D, Loc);
- if (auto *Val = Env.createValue(D.getType()))
- Env.setValue(Loc, *Val);
-}
-
-/// Initializes a global storage value.
-static void initGlobalVar(const Decl &D, Environment &Env) {
+static void insertIfGlobal(const Decl &D,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
if (auto *V = dyn_cast<VarDecl>(&D))
- initGlobalVar(*V, Env);
-}
-
-/// Initializes global storage values that are declared or referenced from
-/// sub-statements of `S`.
-// FIXME: Add support for resetting globals after function calls to enable
-// the implementation of sound analyses.
-static void initGlobalVars(const Stmt &S, Environment &Env) {
- for (auto *Child : S.children()) {
+ if (V->hasGlobalStorage())
+ Vars.insert(V);
+}
+
+static void getFieldsAndGlobalVars(const Decl &D,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
+ insertIfGlobal(D, Fields, Vars);
+ if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
+ for (const auto *B : Decomp->bindings())
+ if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
+ // FIXME: should we be using `E->getFoundDecl()`?
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
+ Fields.insert(FD);
+}
+
+/// Traverses `S` and inserts into `Vars` any global storage values that are
+/// declared in or referenced from sub-statements.
+static void getFieldsAndGlobalVars(const Stmt &S,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
+ for (auto *Child : S.children())
if (Child != nullptr)
- initGlobalVars(*Child, Env);
- }
+ getFieldsAndGlobalVars(*Child, Fields, Vars);
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
- if (DS->isSingleDecl()) {
- initGlobalVar(*DS->getSingleDecl(), Env);
- } else {
+ if (DS->isSingleDecl())
+ getFieldsAndGlobalVars(*DS->getSingleDecl(), Fields, Vars);
+ else
for (auto *D : DS->getDeclGroup())
- initGlobalVar(*D, Env);
- }
+ getFieldsAndGlobalVars(*D, Fields, Vars);
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
- initGlobalVar(*E->getDecl(), Env);
+ insertIfGlobal(*E->getDecl(), Fields, Vars);
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
- initGlobalVar(*E->getMemberDecl(), Env);
+ // FIXME: should we be using `E->getFoundDecl()`?
+ const ValueDecl *VD = E->getMemberDecl();
+ insertIfGlobal(*VD, Fields, Vars);
+ if (const auto *FD = dyn_cast<FieldDecl>(VD))
+ Fields.insert(FD);
+ }
+}
+
+// FIXME: Add support for resetting globals after function calls to enable
+// the implementation of sound analyses.
+void Environment::initVars(llvm::DenseSet<const VarDecl *> Vars) {
+ for (const VarDecl *D : Vars) {
+ if (getStorageLocation(*D, SkipPast::None) != nullptr)
+ continue;
+ auto &Loc = createStorageLocation(*D);
+ setStorageLocation(*D, Loc);
+ if (auto *Val = createValue(D->getType()))
+ setValue(Loc, *Val);
}
}
@@ -216,7 +234,27 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
assert(FuncDecl->getBody() != nullptr);
- initGlobalVars(*FuncDecl->getBody(), *this);
+
+ llvm::DenseSet<const FieldDecl *> Fields;
+ llvm::DenseSet<const VarDecl *> Vars;
+
+ // Look for global variable references in the constructor-initializers.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&DeclCtx)) {
+ for (const auto *Init : CtorDecl->inits()) {
+ if (const auto *M = Init->getAnyMember())
+ Fields.insert(M);
+ const Expr *E = Init->getInit();
+ assert(E != nullptr);
+ getFieldsAndGlobalVars(*E, Fields, Vars);
+ }
+ }
+ getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
+
+ initVars(Vars);
+ // These have to be set before the lines that follow to ensure that create*
+ // work correctly for structs.
+ DACtx.addFieldsReferencedInScope(std::move(Fields));
+
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
auto &ParamLoc = createStorageLocation(*ParamDecl);
@@ -243,15 +281,6 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
setValue(*ThisPointeeLoc, *ThisPointeeVal);
}
}
-
- // Look for global variable references in the constructor-initializers.
- if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&DeclCtx)) {
- for (const auto *Init : CtorDecl->inits()) {
- const Expr *E = Init->getInit();
- assert(E != nullptr);
- initGlobalVars(*E, *this);
- }
- }
}
bool Environment::canDescend(unsigned MaxDepth,
@@ -298,10 +327,24 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
ArrayRef<const Expr *> Args) {
CallStack.push_back(FuncDecl);
- // FIXME: In order to allow the callee to reference globals, we probably need
- // to call `initGlobalVars` here in some way.
+ // FIXME: Share this code with the constructor, rather than duplicating it.
+ llvm::DenseSet<const FieldDecl *> Fields;
+ llvm::DenseSet<const VarDecl *> Vars;
+ // Look for global variable references in the constructor-initializers.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
+ for (const auto *Init : CtorDecl->inits()) {
+ if (const auto *M = Init->getAnyMember())
+ Fields.insert(M);
+ const Expr *E = Init->getInit();
+ assert(E != nullptr);
+ getFieldsAndGlobalVars(*E, Fields, Vars);
+ }
+ }
+ getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
+ initVars(Vars);
+ DACtx->addFieldsReferencedInScope(std::move(Fields));
- auto ParamIt = FuncDecl->param_begin();
+ const auto *ParamIt = FuncDecl->param_begin();
// FIXME: Parameters don't always map to arguments 1:1; examples include
// overloaded operators implemented as member functions, and parameter packs.
@@ -570,7 +613,7 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
const QualType Type = AggregateLoc.getType();
assert(Type->isStructureOrClassType() || Type->isUnionType());
- for (const FieldDecl *Field : getObjectFields(Type)) {
+ for (const FieldDecl *Field : DACtx->getReferencedFields(Type)) {
assert(Field != nullptr);
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
@@ -684,10 +727,8 @@ Value *Environment::createValueUnlessSelfReferential(
if (Type->isStructureOrClassType() || Type->isUnionType()) {
CreatedValuesCount++;
- // FIXME: Initialize only fields that are accessed in the context that is
- // being analyzed.
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
- for (const FieldDecl *Field : getObjectFields(Type)) {
+ for (const FieldDecl *Field : DACtx->getReferencedFields(Type)) {
assert(Field != nullptr);
QualType FieldType = Field->getType();
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 32e9d6f12e44e..465dbc9abeffc 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -283,7 +283,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
} else if (auto *VD = B->getHoldingVar()) {
// Holding vars are used to back the BindingDecls of tuple-like
// types. The holding var declarations appear *after* this statement,
- // so we have to create a location or them here to share with `B`. We
+ // so we have to create a location for them here to share with `B`. We
// don't visit the binding, because we know it will be a DeclRefExpr
// to `VD`.
auto &VDLoc = Env.createStorageLocation(*VD);
diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
index b237f3b83759f..5e55e1be8e475 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
@@ -57,6 +57,8 @@ TEST_F(EnvironmentTest, CreateValueRecursiveType) {
bool X;
Recursive *R;
};
+ // Use both fields to force them to be created with `createValue`.
+ void Usage(Recursive R) { (void)R.X; (void)R.R; }
)cc";
auto Unit =
@@ -74,11 +76,15 @@ TEST_F(EnvironmentTest, CreateValueRecursiveType) {
const QualType *Ty = selectFirst<QualType>("target", Results);
const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results);
ASSERT_TRUE(Ty != nullptr && !Ty->isNull());
- ASSERT_TRUE(R != nullptr);
+ ASSERT_THAT(R, NotNull());
+
+ Results = match(functionDecl(hasName("Usage")).bind("fun"), Context);
+ const auto *Fun = selectFirst<FunctionDecl>("fun", Results);
+ ASSERT_THAT(Fun, NotNull());
// Verify that the struct and the field (`R`) with first appearance of the
// type is created successfully.
- Environment Env(DAContext);
+ Environment Env(DAContext, *Fun);
Value *Val = Env.createValue(*Ty);
ASSERT_NE(Val, nullptr);
StructValue *SVal = clang::dyn_cast<StructValue>(Val);
diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
index c72e8e47dd8e3..e38826cfbe27e 100644
--- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -134,6 +134,11 @@ template <typename AnalysisT> struct AnalysisInputs {
ASTBuildVirtualMappedFiles = std::move(Arg);
return std::move(*this);
}
+ AnalysisInputs<AnalysisT> &&
+ withContextSensitivity() && {
+ EnableContextSensitivity = true;
+ return std::move(*this);
+ }
/// Required. Input code that is analyzed.
llvm::StringRef Code;
@@ -159,6 +164,9 @@ template <typename AnalysisT> struct AnalysisInputs {
ArrayRef<std::string> ASTBuildArgs = {};
/// Optional. Options for building the AST context.
tooling::FileContentMappings ASTBuildVirtualMappedFiles = {};
+ /// Enables context-sensitive analysis when constructing the
+ /// `DataflowAnalysisContext`.
+ bool EnableContextSensitivity = false;
};
/// Returns assertions based on annotations that are present after statements in
@@ -222,7 +230,9 @@ checkDataflow(AnalysisInputs<AnalysisT> AI,
auto &CFCtx = *MaybeCFCtx;
// Initialize states for running dataflow analysis.
- DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>());
+ DataflowAnalysisContext DACtx(
+ std::make_unique<WatchedLiteralsSolver>(),
+ {/*EnableContextSensitiveAnalysis=*/AI.EnableContextSensitivity});
Environment InitEnv(DACtx, *Target);
auto Analysis = AI.MakeAnalysis(Context, InitEnv);
std::function<void(const CFGElement &,
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a9b1f42f20360..68de3865fec09 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -16,6 +16,7 @@
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/LangStandard.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Testing/Support/Error.h"
@@ -39,17 +40,22 @@ void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
llvm::StringRef TargetFun = "target") {
using ast_matchers::hasName;
+ llvm::SmallVector<std::string, 3> ASTBuildArgs = {
+ "-fsyntax-only", "-fno-delayed-template-parsing",
+ "-std=" +
+ std::string(LangStandard::getLangStandardForKind(Std).getName())};
+ auto AI =
+ AnalysisInputs<NoopAnalysis>(Code, hasName(TargetFun),
+ [&Options](ASTContext &C, Environment &) {
+ return NoopAnalysis(C, Options);
+ })
+ .withASTBuildArgs(ASTBuildArgs);
+ if (Options.BuiltinTransferOpts &&
+ Options.BuiltinTransferOpts->ContextSensitiveOpts)
+ AI = std::move(AI).withContextSensitivity();
ASSERT_THAT_ERROR(
checkDataflow<NoopAnalysis>(
- AnalysisInputs<NoopAnalysis>(Code, hasName(TargetFun),
- [Options](ASTContext &C, Environment &) {
- return NoopAnalysis(C, Options);
- })
- .withASTBuildArgs(
- {"-fsyntax-only", "-fno-delayed-template-parsing",
- "-std=" +
- std::string(LangStandard::getLangStandardForKind(Std)
- .getName())}),
+ std::move(AI),
/*VerifyResults=*/
[&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
&Results,
@@ -151,6 +157,7 @@ TEST(TransferTest, StructVarDecl) {
void target() {
A Foo;
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -198,6 +205,7 @@ TEST(TransferTest, StructVarDeclWithInit) {
void target() {
A Foo = Gen();
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -238,11 +246,13 @@ TEST(TransferTest, StructVarDeclWithInit) {
TEST(TransferTest, ClassVarDecl) {
std::string Code = R"(
class A {
+ public:
int Bar;
};
void target() {
A Foo;
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -336,6 +346,10 @@ TEST(TransferTest, SelfReferentialReferenceVarDecl) {
void target() {
A &Foo = getA();
+ (void)Foo.Bar.FooRef;
+ (void)Foo.Bar.FooPtr;
+ (void)Foo.Bar.BazRef;
+ (void)Foo.Bar.BazPtr;
// [[p]]
}
)";
@@ -478,6 +492,10 @@ TEST(TransferTest, SelfReferentialPointerVarDecl) {
void target() {
A *Foo = getA();
+ (void)Foo->Bar->FooRef;
+ (void)Foo->Bar->FooPtr;
+ (void)Foo->Bar->BazRef;
+ (void)Foo->Bar->BazPtr;
// [[p]]
}
)";
@@ -891,7 +909,7 @@ TEST(TransferTest, StructParamDecl) {
};
void target(A Foo) {
- (void)0;
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -1052,6 +1070,9 @@ TEST(TransferTest, DerivedBaseMemberClass) {
int APrivate;
public:
int APublic;
+
+ private:
+ friend void target();
};
class B : public A {
@@ -1060,10 +1081,20 @@ TEST(TransferTest, DerivedBaseMemberClass) {
int BProtected;
private:
int BPrivate;
+
+ private:
+ friend void target();
};
void target() {
B Foo;
+ (void)Foo.ADefault;
+ (void)Foo.AProtected;
+ (void)Foo.APrivate;
+ (void)Foo.APublic;
+ (void)Foo.BDefault;
+ (void)Foo.BProtected;
+ (void)Foo.BPrivate;
// [[p]]
}
)";
@@ -1202,6 +1233,7 @@ TEST(TransferTest, DerivedBaseMemberStructDefault) {
void target() {
B Foo;
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -1525,7 +1557,11 @@ TEST(TransferTest, UnionThisMember) {
int Bar;
void target() {
- (void)0; // [[p]]
+ A a;
+ // Mention the fields to ensure they're included in the analysis.
+ (void)a.Foo;
+ (void)a.Bar;
+ // [[p]]
}
};
)";
@@ -1777,6 +1813,7 @@ TEST(TransferTest, TemporaryObject) {
void target() {
A Foo = A();
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -1814,6 +1851,7 @@ TEST(TransferTest, ElidableConstructor) {
void target() {
A Foo = A();
+ (void)Foo.Bar;
// [[p]]
}
)";
@@ -1851,6 +1889,7 @@ TEST(TransferTest, AssignmentOperator) {
void target() {
A Foo;
A Bar;
+ (void)Foo.Baz;
// [[p1]]
Foo = Bar;
// [[p2]]
@@ -1913,6 +1952,7 @@ TEST(TransferTest, CopyConstructor) {
void target() {
A Foo;
+ (void)Foo.Baz;
A Bar = Foo;
// [[p]]
}
@@ -1958,6 +1998,7 @@ TEST(TransferTest, CopyConstructorWithParens) {
void target() {
A Foo;
+ (void)Foo.Baz;
A Bar((A(Foo)));
// [[p]]
}
@@ -2018,6 +2059,7 @@ TEST(TransferTest, MoveConstructor) {
void target() {
A Foo;
A Bar;
+ (void)Foo.Baz;
// [[p1]]
Foo = std::move(Bar);
// [[p2]]
More information about the cfe-commits
mailing list