Files
clice/tests/unit/Index/USR.cpp
2025-08-16 23:09:13 +08:00

352 lines
8.5 KiB
C++

#include "Support/Logger.h"
#include "Test/Tester.h"
#include "Index/USR.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/RecursiveASTVisitor.h"
namespace clice::testing {
namespace {
struct USRInfo {
llvm::SmallString<128> USR;
uint32_t offset;
clang::Decl* decl;
};
struct GetUSRVisitor : public clang::RecursiveASTVisitor<GetUSRVisitor> {
bool VisitDecl(clang::Decl* decl) {
auto offset = [&]() -> std::optional<std::uint32_t> {
auto loc = decl->getLocation();
if(!loc.isValid())
return std::nullopt;
auto& SM = decl->getASTContext().getSourceManager();
auto [_, offset] = SM.getDecomposedLoc(loc);
return offset;
}();
auto USR = [&]() -> std::optional<llvm::SmallString<128>> {
llvm::SmallString<128> buffer;
if(!clice::index::generateUSRForDecl(decl, buffer)) {
return buffer;
}
return std::nullopt;
}();
if(offset.has_value() && USR.has_value()) {
USRs[*offset] = USRInfo{*USR, *offset, decl};
// log::info("USR: {} at {}:{}", USR->str(), pos->line, pos->character);
}
return true;
}
std::map<std::uint32_t, USRInfo> USRs;
};
struct USRTester : public Tester {
USRTester(llvm::StringRef file, llvm::StringRef content) {
add_main(file, content);
}
void run(llvm::StringRef standard = "-std=c++20") {
Tester::compile(standard);
GetUSRVisitor visitor;
visitor.TraverseDecl(unit->tu());
USRs = std::move(visitor.USRs);
}
llvm::StringRef lookup(llvm::StringRef key) {
auto iter = USRs.find((*this)["main.cpp", key]);
if(iter == USRs.end()) {
log::fatal("USR not found for key: {}", key);
}
return iter->second.USR;
}
std::map<uint32_t, USRInfo> USRs;
};
suite<"USR"> suite = [] {
/// fixme: headers not found
///
/// TEST(Index, USRTemplateClassRequireClause) {
/// llvm::StringRef content = R"cpp(
/// #include <concepts>
/// template <typename T> struct A;
///
/// template <typename T> requires (__is_same(T, float))
/// struct $(1)A<T>;
///
/// template <typename T> requires (__is_same(T, int))
/// struct $(2)A<T>;
///
/// template <typename T> requires (std::same_as<T, double>)
/// struct $(3)A<T> {};
/// )cpp";
///
/// USRTester tester("main.cpp", content);
/// tester.run();
///
/// auto usr1 = tester.lookupUSR("1");
/// auto usr2 = tester.lookupUSR("2");
/// auto usr3 = tester.lookupUSR("3");
///
/// EXPECT_NE(usr1, usr2);
/// EXPECT_NE(usr1, usr3);
/// EXPECT_NE(usr2, usr3);
/// }
test("USRTemplateClassRequireClauseComplex") = [&] {
llvm::StringRef content = R"cpp(
template <typename T> struct A;
template <typename T> requires (__is_same(T, float))
struct $(1)A<T>;
using FLOAT = float;
template <typename T> requires (__is_same(T, FLOAT))
struct $(2)A<T>;
template <typename T> requires (__is_same(T, A<int>))
struct $(11)A<T>;
template <typename T> requires (__is_same(T, A<float>))
struct $(12)A<T>;
template <typename T> requires (__is_same(T, A<FLOAT>))
struct $(13)A<T>;
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 == usr2);
auto usr11 = tester.lookup("11");
auto usr12 = tester.lookup("12");
auto usr13 = tester.lookup("13");
expect(that % usr11 != usr12);
expect(that % usr12 == usr13);
};
test("USRTemplateClassConceptConstraint") = [&] {
llvm::StringRef content = R"cpp(
template<typename T>
concept C = requires(T t) { true; };
template<typename T, typename U>
struct A;
template<typename T, C U>
struct $(1)A<T, U>;
template<C T, typename U>
struct $(2)A<T, U>;
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 != usr2);
};
test("USRTemplateClassConceptConstraintPack") = [&] {
llvm::StringRef content1 = R"cpp(
template<typename... Ts>
struct $(1)A;
)cpp";
llvm::StringRef content2 = R"cpp(
template<typename T>
concept C = requires(T t) { true; };
template<C... Ts>
struct $(2)A;
)cpp";
USRTester tester1("main.cpp", content1);
tester1.run();
USRTester tester2("main.cpp", content2);
tester2.run();
auto usr1 = tester1.lookup("1");
auto usr2 = tester2.lookup("2");
expect(that % usr1 != usr2);
};
test("USRTemplateArgumentExpr") = [&] {
llvm::StringRef content = R"cpp(
template <typename T, int N> struct C;
template <int N> struct $(1)C<float, N>;
template <int M> struct $(2)C<float, M>;
template <char c> struct $(3)C<float, c>;
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
auto usr3 = tester.lookup("3");
expect(that % usr1 == usr2);
expect(that % usr1 != usr3);
};
test("USRTemplateFunctionRequireClause") = [&] {
llvm::StringRef content = R"cpp(
template<typename T>
void $(1)func(T t) requires (sizeof(T) == 4) {};
template<typename T>
void $(2)func(T t) requires (sizeof(T) == 8) {};
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 != usr2);
};
test("USRTemplateVarConceptConstraint") = [&] {
llvm::StringRef content1 = R"cpp(
template <typename T>
constexpr T $(1)pi = 3.14;
)cpp";
llvm::StringRef content2 = R"cpp(
template <typename T>
concept integral = requires (T t) { t + 1; };
template <integral T>
constexpr T $(2)pi = 3;
)cpp";
USRTester tester1("main.cpp", content1);
tester1.run();
USRTester tester2("main.cpp", content2);
tester2.run();
auto usr1 = tester1.lookup("1");
auto usr2 = tester2.lookup("2");
expect(that % usr1 != usr2);
};
test("USRCTAD") = [&] {
llvm::StringRef content = R"cpp(
template<typename T>
struct array {};
template<typename U, array arr>
struct $(1)L;
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
expect(that % usr1.contains("array"));
};
test("USRTemplateParamObject") = [&] {
llvm::StringRef content = R"cpp(
template<typename T> struct array {
constexpr array(T x_) : x(x_) {}
int x;
};
template<array U> struct L;
template<> struct $(1)L<{1}>;
template<> struct $(2)L<{2}>;
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 != usr2);
};
test("USRNTTPConstraint") = [&] {
llvm::StringRef content1 = R"cpp(
template<auto N> struct $(1)M;
)cpp";
llvm::StringRef content2 = R"cpp(
template<typename T> concept C = requires {requires true;};
template<C auto N> struct $(2)M;
)cpp";
USRTester tester1("main.cpp", content1);
tester1.run();
USRTester tester2("main.cpp", content2);
tester2.run();
auto usr1 = tester1.lookup("1");
auto usr2 = tester2.lookup("2");
expect(that % usr1 != usr2);
};
test("USRDependentTemplateName") = [&] {
llvm::StringRef content = R"cpp(
template <typename MetaFun>
struct X {
template <template <typename, typename> class Tmpl>
class Y;
template <>
class $(1)Y<MetaFun::template apply>;
template <>
class $(2)Y<MetaFun::template apply2>;
};
)cpp";
USRTester tester("main.cpp", content);
tester.run();
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 != usr2);
};
test("USRDeducingThis") = [&] {
llvm::StringRef content = R"cpp(
class A {
public:
template <typename Self>
void $(1)foo(this Self&& s);
template <typename Self>
void $(2)foo(Self&& s);
};
)cpp";
USRTester tester("main.cpp", content);
tester.run("-std=c++23");
auto usr1 = tester.lookup("1");
auto usr2 = tester.lookup("2");
expect(that % usr1 != usr2);
};
};
} // namespace
} // namespace clice::testing