#include "Test/Test.h" #include "Test/Tester.h" #include "Index/USR.h" #include "Support/Logging.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}; // LOG_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()) { LOG_FATAL("USR not found for key: {}", key); } return iter->second.USR; } std::map USRs; }; TEST_SUITE(USR) { /// 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_CASE(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"); ASSERT_EQ(usr1, usr2); auto usr11 = tester.lookup("11"); auto usr12 = tester.lookup("12"); auto usr13 = tester.lookup("13"); ASSERT_NE(usr11, usr12); ASSERT_EQ(usr12, usr13); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(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"); ASSERT_EQ(usr1, usr2); ASSERT_NE(usr1, usr3); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(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"); ASSERT_TRUE(usr1.contains("array")); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(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"); ASSERT_NE(usr1, usr2); }; TEST_CASE(USRDependentTemplateName) { llvm::StringRef content = R"cpp( template struct X { template