Skip to content

[clang][dataflow] Add matcher for pointer-like types to be cached #132314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ namespace clang::dataflow {
/// for `std::optional`, we assume the (Matcher, TransferFunction) case
/// with custom handling is ordered early so that these generic cases
/// do not trigger.
ast_matchers::StatementMatcher isPointerLikeOperatorStar();
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
return HasStarAndArrow && (HasGet || HasValue);
}

AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
bool HasGet = false;
bool HasValue = false;
bool HasStarAndArrow =
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
return HasStarAndArrow;
}

} // namespace

namespace clang::dataflow {
Expand All @@ -140,6 +148,22 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
ofClass(smartPointerClassWithGetOrValue()))));
}

ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
return cxxOperatorCallExpr(
hasOverloadedOperatorName("*"),
callee(cxxMethodDecl(parameterCountIs(0),
returns(hasCanonicalType(referenceType())),
ofClass(pointerClass()))));
}

ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {
return cxxOperatorCallExpr(
hasOverloadedOperatorName("->"),
callee(cxxMethodDecl(parameterCountIs(0),
returns(hasCanonicalType(pointerType())),
ofClass(pointerClass()))));
}

ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
return cxxMemberCallExpr(callee(cxxMethodDecl(
parameterCountIs(0), returns(hasCanonicalType(referenceType())),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,62 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) {
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isPointerLikeOperatorStar()));

EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P.get()->i; }",
isSmartPointerLikeGetMethodCall()));

EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));
}

TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->() const;
T& operator*() const;
};
} // namespace std

template <class T>
using UniquePtrAlias = std::unique_ptr<T>;

struct S { int i; };
)cc");

EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isPointerLikeOperatorStar()));

EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_FALSE(matches(Decls,
"int target(UniquePtrAlias<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
Expand All @@ -75,15 +122,25 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S, T> P) { return (*P).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S, T> P) { return (*P).i; }",
isPointerLikeOperatorStar()));

EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S, T> P) { return P->j; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S, T> P) { return P->j; }",
isPointerLikeOperatorArrow()));
// The class matching arguably accidentally matches, just because the
// instantiation is with S, S. Hopefully doesn't happen too much in real code
// with such operator* and operator-> overloads.
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S, S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S, S> P) { return P->i; }",
isPointerLikeOperatorArrow()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
Expand All @@ -103,6 +160,9 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
EXPECT_FALSE(
matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_FALSE(
matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
isPointerLikeOperatorStar()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
Expand All @@ -122,9 +182,17 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return (*P).i; }",
isPointerLikeOperatorStar()));

EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_FALSE(
matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }",
isSmartPointerLikeGetMethodCall()));
Expand All @@ -146,6 +214,10 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeGetMethodCall()));
Expand All @@ -171,15 +243,31 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
EXPECT_TRUE(matches(
Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(
Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
isPointerLikeOperatorStar()));

EXPECT_TRUE(matches(
Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(
Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
isPointerLikeOperatorStar()));

EXPECT_TRUE(matches(
Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(
Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(
Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(
Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(
Decls,
"int target(std::optional<S> &NonConst) { return NonConst.value().i; }",
Expand Down Expand Up @@ -214,16 +302,34 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
Decls,
"int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(
Decls,
"int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
isPointerLikeOperatorStar()));

EXPECT_TRUE(matches(
Decls,
"int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
isSmartPointerLikeOperatorStar()));
EXPECT_TRUE(matches(
Decls,
"int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
isPointerLikeOperatorStar()));

EXPECT_TRUE(matches(
Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(
Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(
Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(
Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(
Decls,
"int target(HasGetAndValue<S> &NonConst) { return NonConst.value().i; }",
Expand Down