#include "Support/Logging.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 { bool VisitDecl(clang::Decl* decl) { auto offset = [&]() -> std::optional { 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> 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}; // logging::info("USR: {} at {}:{}", USR->str(), pos->line, pos->character); } return true; } std::map 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()) { logging::fatal("USR not found for key: {}", key); } return iter->second.USR; } std::map USRs; }; suite<"USR"> suite = [] { /// fixme: headers not found /// /// TEST(Index, USRTemplateClassRequireClause) { /// llvm::StringRef content = R"cpp( /// #include /// template struct A; /// /// template requires (__is_same(T, float)) /// struct $(1)A; /// /// template requires (__is_same(T, int)) /// struct $(2)A; /// /// template requires (std::same_as) /// struct $(3)A {}; /// )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 struct A; template requires (__is_same(T, float)) struct $(1)A; using FLOAT = float; template requires (__is_same(T, FLOAT)) struct $(2)A; template requires (__is_same(T, A)) struct $(11)A; template requires (__is_same(T, A)) struct $(12)A; template requires (__is_same(T, A)) struct $(13)A; )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 concept C = requires(T t) { true; }; template struct A; template struct $(1)A; template struct $(2)A; )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 struct $(1)A; )cpp"; llvm::StringRef content2 = R"cpp( template concept C = requires(T t) { true; }; template 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 struct C; template struct $(1)C; template struct $(2)C; template struct $(3)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 void $(1)func(T t) requires (sizeof(T) == 4) {}; template 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 constexpr T $(1)pi = 3.14; )cpp"; llvm::StringRef content2 = R"cpp( template concept integral = requires (T t) { t + 1; }; template 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 struct array {}; template 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 struct array { constexpr array(T x_) : x(x_) {} int x; }; template 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 struct $(1)M; )cpp"; llvm::StringRef content2 = R"cpp( template concept C = requires {requires true;}; template 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 struct X { template