llvm-project/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
Florian Mayer 842f25a5fb
[NFC] [dataflow] generalize smart pointer caching (#133350)
This allows us to use it for classes that use other names than get /
value.
2025-03-28 17:51:30 -07:00

397 lines
13 KiB
C++

//===- unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp ==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Testing/TestAST.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
namespace clang::dataflow {
namespace {
using clang::ast_matchers::match;
template <typename MatcherT>
bool matches(llvm::StringRef Decls, llvm::StringRef TestInput,
MatcherT Matcher) {
TestAST InputAST(Decls.str() + TestInput.str());
return !match(Matcher, InputAST.context()).empty();
}
TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->() const;
T& operator*() const;
T* get() const;
};
} // namespace std
template <class T>
using UniquePtrAlias = std::unique_ptr<T>;
struct S { int i; };
)cc");
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) {
llvm::StringRef Decls(R"cc(
namespace std {
// unique_ptr isn't really like this, but we aren't matching by name
template <class T, class U>
struct unique_ptr {
U* operator->() const;
T& operator*() const;
T* get() const;
};
} // namespace std
struct S { int i; };
struct T { int j; };
)cc");
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) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->() const;
T& operator*(int x) const;
T* get() const;
};
} // namespace std
struct S { int i; };
)cc");
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) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->();
T& operator*();
T* get();
};
} // namespace std
struct S { int i; };
)cc");
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()));
}
TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->();
T* get();
};
} // namespace std
struct S { int i; };
)cc");
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()));
}
TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct optional {
const T* operator->() const;
T* operator->();
const T& operator*() const;
T& operator*();
const T& value() const;
T& value();
};
} // namespace std
struct S { int i; };
)cc");
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; }",
isSmartPointerLikeValueMethodCall()));
EXPECT_TRUE(matches(
Decls,
"int target(const std::optional<S> &Const) { return Const.value().i; }",
isSmartPointerLikeValueMethodCall()));
}
TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
llvm::StringRef Decls(R"cc(
template <class T>
struct HasGetAndValue {
using pointer_t = T*;
using reference_t = T&;
const pointer_t operator->() const;
pointer_t operator->();
const reference_t operator*() const;
reference_t operator*();
const reference_t value() const;
reference_t value();
const pointer_t get() const;
pointer_t get();
};
struct S { int i; };
)cc");
EXPECT_TRUE(matches(
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; }",
isSmartPointerLikeValueMethodCall()));
EXPECT_TRUE(matches(
Decls,
"int target(const HasGetAndValue<S> &Const) { return Const.value().i; }",
isSmartPointerLikeValueMethodCall()));
EXPECT_TRUE(matches(
Decls,
"int target(HasGetAndValue<S> &NonConst) { return NonConst.get()->i; }",
isSmartPointerLikeGetMethodCall()));
EXPECT_TRUE(matches(
Decls,
"int target(const HasGetAndValue<S> &Const) { return Const.get()->i; }",
isSmartPointerLikeGetMethodCall()));
}
TEST(SmartPointerAccessorCachingTest, Renamed) {
llvm::StringRef Decls(R"cc(
namespace std {
template <class T>
struct unique_ptr {
T* operator->() const;
T& operator*() const;
T* Get() const;
T& Value() const;
};
} // namespace std
template <class T>
using UniquePtrAlias = std::unique_ptr<T>;
struct S { int i; };
)cc");
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; }",
isPointerLikeOperatorArrow()));
EXPECT_TRUE(matches(Decls,
"int target(std::unique_ptr<S> P) { return P.Get()->i; }",
isSmartPointerLikeGetMethodCall("Get")));
EXPECT_TRUE(matches(Decls,
"int target(UniquePtrAlias<S> P) { return P.Get()->i; }",
isSmartPointerLikeGetMethodCall("Get")));
EXPECT_TRUE(
matches(Decls, "int target(std::unique_ptr<S> P) { return P.Value().i; }",
isSmartPointerLikeValueMethodCall("Value")));
EXPECT_TRUE(matches(Decls,
"int target(UniquePtrAlias<S> P) { return P.Value().i; }",
isSmartPointerLikeValueMethodCall("Value")));
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
isPointerLikeOperatorArrow()));
}
} // namespace
} // namespace clang::dataflow