[clang][dataflow] Add support for nested method calls.

Extend the context-sensitive analysis to handle a call to a method (of the same
class) from within a method. That, is a member-call expression through `this`.

Differential Revision: https://reviews.llvm.org/D134432
This commit is contained in:
Yitzhak Mandelbaum 2022-09-22 12:42:23 +00:00
parent d0aeb74e88
commit 0b12efc7a4
2 changed files with 73 additions and 3 deletions

View File

@ -216,7 +216,10 @@ Environment Environment::pushCall(const CallExpr *Call) const {
if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {
if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {
Env.ThisPointeeLoc = getStorageLocation(*Arg, SkipPast::Reference);
if (!isa<CXXThisExpr>(Arg))
Env.ThisPointeeLoc = getStorageLocation(*Arg, SkipPast::Reference);
// Otherwise (when the argument is `this`), retain the current
// environment's `ThisPointeeLoc`.
}
}

View File

@ -4359,7 +4359,7 @@ TEST(TransferTest, ContextSensitiveMethodSetter) {
std::string Code = R"(
class MyClass {
public:
bool setField(bool Val) { Field = Val; }
void setField(bool Val) { Field = Val; }
bool Field;
};
@ -4392,7 +4392,7 @@ TEST(TransferTest, ContextSensitiveMethodGetterAndSetter) {
class MyClass {
public:
bool getField() { return Field; }
bool setField(bool Val) { Field = Val; }
void setField(bool Val) { Field = Val; }
private:
bool Field;
@ -4421,6 +4421,73 @@ TEST(TransferTest, ContextSensitiveMethodGetterAndSetter) {
{TransferOptions{ContextSensitiveOptions{}}});
}
TEST(TransferTest, ContextSensitiveMethodTwoLayersVoid) {
std::string Code = R"(
class MyClass {
public:
void Inner() { MyField = true; }
void Outer() { Inner(); }
bool MyField;
};
void target() {
MyClass MyObj;
MyObj.Outer();
bool Foo = MyObj.MyField;
// [[p]]
}
)";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));;
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
},
{TransferOptions{ContextSensitiveOptions{}}});
}
TEST(TransferTest, ContextSensitiveMethodTwoLayersReturn) {
std::string Code = R"(
class MyClass {
public:
bool Inner() { return MyField; }
bool Outer() { return Inner(); }
bool MyField;
};
void target() {
MyClass MyObj;
MyObj.MyField = true;
bool Foo = MyObj.Outer();
// [[p]]
}
)";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));;
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
},
{TransferOptions{ContextSensitiveOptions{}}});
}
TEST(TransferTest, ContextSensitiveConstructorBody) {
std::string Code = R"(
class MyClass {