Skip to content
Open
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,6 +58,8 @@ 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 isPointerLikeConstructor();
ast_matchers::StatementMatcher isSmartPointerLikeConstructor();
ast_matchers::StatementMatcher isPointerLikeOperatorStar();
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
Expand All @@ -80,6 +82,8 @@ isSmartPointerLikeGetMethodCall(clang::StringRef MethodName = "get");
const FunctionDecl *
getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE);

const FunctionDecl *
getCanonicalSmartPointerLikeOperatorCalleeForType(const CXXRecordDecl *RD);
/// A transfer function for `operator*` (and `value`) calls that can be
/// cached. Runs the `InitializeLoc` callback to initialize any new
/// StorageLocations.
Expand Down Expand Up @@ -163,6 +167,20 @@ void transferSmartPointerLikeCachedDeref(
State.Env.setStorageLocation(*DerefExpr, LocForValue);
}

// This was introduced after the QualType was added to InitializeLoc, so
// we don't provide a compatibility wrapper.
template <typename LatticeT>
void transferSmartPointerLikeConstructor(
const CXXConstructExpr *ConstructOperator,
RecordStorageLocation *SmartPointerLoc, TransferState<LatticeT> &State,
llvm::function_ref<void(QualType, StorageLocation &)> InitializeLoc) {
const FunctionDecl *CanonicalCallee =
getCanonicalSmartPointerLikeOperatorCalleeForType(
ConstructOperator->getType()->getAsCXXRecordDecl());
State.Lattice.getOrCreateConstMethodReturnStorageLocation(
*SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
}

template <typename LatticeT>
void transferSmartPointerLikeCachedGet(
const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc,
Expand Down
33 changes: 25 additions & 8 deletions clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ AST_MATCHER(clang::CXXRecordDecl, pointerClass) {

namespace clang::dataflow {

ast_matchers::StatementMatcher isSmartPointerLikeConstructor() {
using namespace ast_matchers;
return cxxConstructExpr(hasType(hasCanonicalType(qualType(
hasDeclaration(cxxRecordDecl(smartPointerClassWithGetOrValue()))))));
}

ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() {
return cxxOperatorCallExpr(
hasOverloadedOperatorName("*"),
Expand All @@ -145,6 +151,12 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
ofClass(smartPointerClassWithGetOrValue()))));
}

ast_matchers::StatementMatcher isPointerLikeConstructor() {
using namespace ast_matchers;
return cxxConstructExpr(hasType(hasCanonicalType(
qualType(hasDeclaration(cxxRecordDecl(pointerClass()))))));
}

ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
return cxxOperatorCallExpr(
hasOverloadedOperatorName("*"),
Expand Down Expand Up @@ -177,15 +189,8 @@ isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) {
}

const FunctionDecl *
getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {
getCanonicalSmartPointerLikeOperatorCalleeForType(const CXXRecordDecl *RD) {
const FunctionDecl *CanonicalCallee = nullptr;
const CXXMethodDecl *Callee =
cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
if (Callee == nullptr)
return nullptr;
const CXXRecordDecl *RD = Callee->getParent();
if (RD == nullptr)
return nullptr;
for (const auto *MD : RD->methods()) {
if (MD->getOverloadedOperator() == OO_Star && MD->isConst() &&
MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) {
Expand All @@ -196,4 +201,16 @@ getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {
return CanonicalCallee;
}

const FunctionDecl *
getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {
const CXXMethodDecl *Callee =
cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
if (Callee == nullptr)
return nullptr;
const CXXRecordDecl *RD = Callee->getParent();
if (RD == nullptr)
return nullptr;
return getCanonicalSmartPointerLikeOperatorCalleeForType(RD);
}

} // namespace clang::dataflow
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) {
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_TRUE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_TRUE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) {
Expand Down Expand Up @@ -101,6 +110,15 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) {
isSmartPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_TRUE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
Expand Down Expand Up @@ -141,6 +159,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S, S> P) { return P->i; }",
isPointerLikeOperatorArrow()));

EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S, T>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S, T>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

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

EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
Expand Down Expand Up @@ -196,6 +232,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
EXPECT_FALSE(
matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }",
isSmartPointerLikeGetMethodCall()));

EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
Expand All @@ -221,6 +266,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
EXPECT_FALSE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P->i; }",
isSmartPointerLikeGetMethodCall()));

EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_FALSE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
Expand Down Expand Up @@ -276,6 +330,13 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
Decls,
"int target(const std::optional<S> &Const) { return Const.value().i; }",
isSmartPointerLikeValueMethodCall()));

EXPECT_TRUE(matches(
Decls, "std::optional<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
EXPECT_TRUE(matches(
Decls, "std::optional<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
Expand Down Expand Up @@ -329,6 +390,9 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
EXPECT_TRUE(matches(
Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
isPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(
Decls, "HasGetAndValue<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));

EXPECT_TRUE(matches(
Decls,
Expand All @@ -346,6 +410,9 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
Decls,
"int target(const HasGetAndValue<S> &Const) { return Const.get()->i; }",
isSmartPointerLikeGetMethodCall()));
EXPECT_TRUE(matches(
Decls, "HasGetAndValue<S>& Helper(); int target() { auto S = Helper(); }",
isSmartPointerLikeConstructor()));
}

TEST(SmartPointerAccessorCachingTest, Renamed) {
Expand Down Expand Up @@ -390,6 +457,11 @@ TEST(SmartPointerAccessorCachingTest, Renamed) {

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

EXPECT_TRUE(matches(
Decls,
"std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
isPointerLikeConstructor()));
}

} // namespace
Expand Down