diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 8617cf4c8ca4a..200a981584bb7 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -175,12 +175,16 @@ class TransferVisitor : public ConstStmtVisitor { const ValueDecl *VD = S->getDecl(); assert(VD != nullptr); - // `DeclRefExpr`s to fields and non-static methods aren't glvalues, and - // there's also no sensible `Value` we can assign to them, so skip them. - if (isa(VD)) - return; - if (auto *Method = dyn_cast(VD); - Method && !Method->isStatic()) + // Some `DeclRefExpr`s aren't glvalues, so we can't associate them with a + // `StorageLocation`, and there's also no sensible `Value` that we can + // assign to them. Examples: + // - Non-static member variables + // - Non static member functions + // Note: Member operators are an exception to this, but apparently only + // if the `DeclRefExpr` is used within the callee of a + // `CXXOperatorCallExpr`. In other cases, for example when applying the + // address-of operator, the `DeclRefExpr` is a prvalue. + if (!S->isGLValue()) return; auto *DeclLoc = Env.getStorageLocation(*VD); diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 4c31de3c8085b..ef28f2f233b84 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -5588,6 +5588,59 @@ TEST(TransferTest, BuiltinFunctionModeled) { }); } +// Check that a callee of a member operator call is modeled as a `PointerValue`. +// Member operator calls are unusual in that their callee is a pointer that +// stems from a `FunctionToPointerDecay`. In calls to non-operator non-static +// member functions, the callee is a `MemberExpr` (which does not have pointer +// type). +// We want to make sure that we produce a pointer value for the callee in this +// specific scenario and that its storage location is durable (for convergence). +TEST(TransferTest, MemberOperatorCallModelsPointerForCallee) { + std::string Code = R"( + struct S { + bool operator!=(S s); + }; + void target() { + S s; + (void)(s != s); + (void)(s != s); + // [[p]] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + using ast_matchers::selectFirst; + using ast_matchers::match; + using ast_matchers::traverse; + using ast_matchers::cxxOperatorCallExpr; + + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto Matches = match( + traverse(TK_AsIs, cxxOperatorCallExpr().bind("call")), ASTCtx); + + ASSERT_EQ(Matches.size(), 2); + + auto *Call1 = Matches[0].getNodeAs("call"); + auto *Call2 = Matches[1].getNodeAs("call"); + + ASSERT_THAT(Call1, NotNull()); + ASSERT_THAT(Call2, NotNull()); + + EXPECT_EQ(cast(Call1->getCallee())->getCastKind(), + CK_FunctionToPointerDecay); + EXPECT_EQ(cast(Call2->getCallee())->getCastKind(), + CK_FunctionToPointerDecay); + + auto *Ptr1 = cast(Env.getValue(*Call1->getCallee())); + auto *Ptr2 = cast(Env.getValue(*Call2->getCallee())); + + ASSERT_EQ(&Ptr1->getPointeeLoc(), &Ptr2->getPointeeLoc()); + }); +} + // Check that fields of anonymous records are modeled. TEST(TransferTest, AnonymousStruct) { std::string Code = R"(