diff --git a/include/Compiler/Resolver.h b/include/Compiler/Resolver.h index d09b2695..4d31d8e8 100644 --- a/include/Compiler/Resolver.h +++ b/include/Compiler/Resolver.h @@ -7,6 +7,12 @@ namespace clice { +/// This class is used to resolve dependent names in the AST. +/// For dependent names, we cannot know the any information about the name until +/// the template is instantiated. This can be frustrating, you cannot get +/// completion, you cannot get go-to-definition, etc. To avoid this, we just use +/// some heuristics to simplify the dependent names as normal type/expression. +/// For example, `std::vector::value_type` can be simplified as `T`. class TemplateResolver { public: TemplateResolver(clang::Sema& sema) : sema(sema) {} @@ -17,61 +23,4 @@ private: clang::Sema& sema; }; -/// This class is used to resolve dependent names in the AST. -/// For dependent names, we cannot know the any information about the name until -/// the template is instantiated. This can be frustrating, you cannot get -/// completion, you cannot get go-to-definition, etc. To avoid this, we just use -/// some heuristics to simplify the dependent names as normal type/expression. -/// For example, `std::vector::value_type` can be simplified as `T`. -class DependentNameResolver { - -public: - DependentNameResolver(clang::Sema& sema, clang::ASTContext& context) : sema(sema), context(context) {} - - clang::QualType resolve(clang::NamedDecl* ND); - - clang::QualType resolve(clang::QualType type); - - clang::QualType resolve(const clang::DependentNameType* DNT); - - clang::QualType resolve(const clang::DependentTemplateSpecializationType* DTST); - - /// lookup member in a given nested name specifier - bool lookup(llvm::SmallVector& result, - const clang::NestedNameSpecifier* NNS, - const clang::IdentifierInfo* II); - - bool lookup(llvm::SmallVector& result, - const clang::QualType type, - const clang::IdentifierInfo* II); - - // lookup member in a given class template - // FIXME: search in base classes - bool lookup(llvm::SmallVector& result, - clang::ClassTemplateDecl* CTD, - const clang::IdentifierInfo* II, - llvm::ArrayRef arguments); - - std::vector resugar(llvm::ArrayRef arguments); - - /// we use `Sema::SubstType` to substitute the template arguments in dependent type. - /// but it doesn't substitute the template arguments in alias type. - /// i.e. `typename base::type`, when base is `std::vector`, it will ignore the `T`. - /// so before actually substituting the type, we need to dealias the type. - clang::QualType dealias(clang::QualType type); - - /// replace the template arguments in the type, using the arguments in the frame - clang::QualType substitute(clang::QualType type); - -private: - struct Frame { - clang::NamedDecl* decl; - std::vector arguments; - }; - - clang::Sema& sema; - clang::ASTContext& context; - std::vector frames; -}; - } // namespace clice diff --git a/src/Compiler/Resolver.cpp b/src/Compiler/Resolver.cpp index c5c21cd3..2744bc98 100644 --- a/src/Compiler/Resolver.cpp +++ b/src/Compiler/Resolver.cpp @@ -5,6 +5,37 @@ namespace clice { namespace { +/// `Sema::SubstType` will not substitute in aliased types, e.g. +/// ```cpp +/// template +/// struct A { +/// using base = std::vector; +/// using type = typename base::reference; +/// }; +/// ``` +/// In this case, if you call `SubstType` on `type`, base will keep the same. +/// So we need to dealias the type first before calling `SubstType`. +class DealiasOnly : public clang::TreeTransform { +public: + DealiasOnly(clang::Sema& sema) : TreeTransform(sema), context(sema.getASTContext()) {} + + clang::QualType TransformTypedefType(clang::TypeLocBuilder& TLB, clang::TypedefTypeLoc TL) { + if(clang::TypedefNameDecl* TND = TL.getTypedefNameDecl()) { + auto type = TransformType(TND->getUnderlyingType()); + if(auto ET = llvm::dyn_cast(type)) { + type = ET->getNamedType(); + } + TLB.pushTrivial(context, type, {}); + return type; + } + + return clang::QualType(); + } + +private: + clang::ASTContext& context; +}; + class TemplateResolverImpl : public clang::TreeTransform { private: struct Frame { @@ -18,14 +49,20 @@ private: public: TemplateResolverImpl(clang::Sema& sema) : TreeTransform(sema), sema(sema), context(sema.getASTContext()) {} - /// Because Sema::SubsType does not handle alias types, meaning that if the aliased - /// type contains template parameters, it will not substitute them either, so we need - /// to remove the alias ourselves. + clang::QualType TransformElaboratedType(clang::TypeLocBuilder& TLB, clang::ElaboratedTypeLoc TL) { + clang::QualType type = TransformType(TL.getNamedTypeLoc().getType()); + TLB.pushTrivial(context, type, {}); + return type; + } + clang::QualType TransformTypedefType(clang::TypeLocBuilder& TLB, clang::TypedefTypeLoc TL) { if(clang::TypedefNameDecl* TND = TL.getTypedefNameDecl()) { - auto TSI = TransformType(TND->getTypeSourceInfo()); - TLB.pushTrivial(context, TSI->getType(), {}); - return TSI->getType(); + auto type = TransformType(TND->getUnderlyingType()); + if(auto ET = llvm::dyn_cast(type)) { + type = ET->getNamedType(); + } + TLB.pushTrivial(context, type, {}); + return type; } return clang::QualType(); @@ -67,20 +104,35 @@ public: return clang::QualType(); } - // clang::QualType TransformDependentTemplateSpecializationType(clang::TypeLocBuilder& TLB, - // clang::DependentTemplateSpecializationTypeLoc TL, - // bool DeducedTSTContext = false) { - // // Base::TransformDependentTemplateSpecializationType() - // // DependentNameResolver resolver(sema, context); - // // auto result = resolver.resolve(TL.getType()); - // // TLB.pushTrivial(context, result, {}); - // // TODO: - // } + using Base::TransformDependentTemplateSpecializationType; + clang::QualType TransformDependentTemplateSpecializationType(clang::TypeLocBuilder& TLB, + clang::DependentTemplateSpecializationTypeLoc TL) { + auto NNS = TransformNestedNameSpecifierLoc(TL.getQualifierLoc()).getNestedNameSpecifier(); + auto II = TL.getTypePtr()->getIdentifier(); + + Names names; + auto copy = frames; + if(lookup(names, NNS, II) && names.size() == 1) { + if(auto TATD = llvm::dyn_cast(names.front())) { + // FIXME: Transform template arguments + frames.emplace_back(TATD, TL.getTypePtr()->template_arguments()); + clang::QualType type = TransformType(substitute(TATD->getTemplatedDecl()->getUnderlyingType())); + TLB.pushTrivial(context, type, {}); + return type; + } + } + + frames = std::move(copy); + TLB.push(TL.getType()); + return TL.getType(); + } + + /// look up the name in the given type. bool lookup(Names& names, clang::QualType type, const clang::IdentifierInfo* II) { type = TransformType(type); - llvm::outs() << "lookup {" + II->getName() + "} in {" + type.getAsString() + "}\n"; + llvm::outs() << "lookup { " + II->getName() + " } in { " + type.getAsString() + " }\n"; clang::TemplateDecl* TD; llvm::ArrayRef args; @@ -108,6 +160,7 @@ public: return true; } + /// look up the name in the given nested name specifier. bool lookup(Names& names, const clang::NestedNameSpecifier* NNS, const clang::IdentifierInfo* II) { switch(NNS->getKind()) { case clang::NestedNameSpecifier::Identifier: { @@ -129,6 +182,9 @@ public: return false; } + /// look up the name in the given class template. We first try to find the name in the main + /// template, if failed, we try to find the name in the base classes, if still failed, we try to find + /// the name in the partial specializations. bool lookup(Names& names, clang::ClassTemplateDecl* CTD, const clang::IdentifierInfo* II, @@ -190,7 +246,7 @@ public: } clang::QualType substitute(clang::QualType type) { - type = TransformType(type); + type = DealiasOnly(sema).TransformType(type); clang::MultiLevelTemplateArgumentList list; for(auto begin = frames.rbegin(), end = frames.rend(); begin != end; ++begin) { @@ -206,7 +262,7 @@ public: } auto result = sema.SubstType(type, list, {}, {}); - llvm::outs() << "substitute {" + type.getAsString() + "} to {" + result.getAsString() + "}\n"; + llvm::outs() << "substitute { " + type.getAsString() + " } to { " + result.getAsString() + " }\n"; frames.clear(); return result; } @@ -232,303 +288,4 @@ clang::QualType TemplateResolver::resolve(clang::QualType type) { return resolver.TransformType(type); } -clang::QualType DependentNameResolver::resolve(clang::NamedDecl* ND) { - if(auto TAD = llvm::dyn_cast(ND)) { - return resolve(substitute(TAD->getUnderlyingType())); - } else if(auto TND = llvm::dyn_cast(ND)) { - return resolve(substitute(TND->getUnderlyingType())); - } else { - std::terminate(); - } -} - -clang::QualType DependentNameResolver::resolve(clang::QualType type) { - if(!type->isDependentType()) { - return type; - } - - type = type.getDesugaredType(context); - - if(auto TST = type->getAs()) { - std::vector args; - for(auto arg: TST->template_arguments()) { - if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) { - args.push_back(resolve(arg.getAsType())); - } else { - args.push_back(arg); - } - } - return context.getTemplateSpecializationType(TST->getTemplateName(), args); - } else if(auto DNT = llvm::dyn_cast(type)) { - return resolve(DNT); - } else if(auto DTST = llvm::dyn_cast(type)) { - return resolve(DTST); - } else if(auto LVRT = llvm::dyn_cast(type)) { - return context.getLValueReferenceType(resolve(LVRT->getPointeeType())); - } else { - return type; - } -} - -clang::QualType DependentNameResolver::resolve(const clang::DependentNameType* DNT) { - llvm::SmallVector result; - if(lookup(result, DNT->getQualifier(), DNT->getIdentifier()) && result.size() == 1) { - return resolve(result.front()); - } else { - DNT->dump(); - std::terminate(); - } -} - -clang::QualType DependentNameResolver::resolve(const clang::DependentTemplateSpecializationType* DTST) { - llvm::SmallVector result; - if(lookup(result, DTST->getQualifier(), DTST->getIdentifier()) && result.size() == 1) { - if(auto TATD = llvm::dyn_cast(result.front())) { - frames.emplace_back(TATD, resugar(DTST->template_arguments())); - return resolve(substitute(TATD->getTemplatedDecl()->getUnderlyingType())); - } - } - - DTST->dump(); - std::terminate(); -} - -bool DependentNameResolver::lookup(llvm::SmallVector& result, - const clang::NestedNameSpecifier* NNS, - const clang::IdentifierInfo* II) { - switch(NNS->getKind()) { - // prefix is an identifier, e.g. <...>::name:: - case clang::NestedNameSpecifier::SpecifierKind::Identifier: { - if(lookup(result, NNS->getPrefix(), NNS->getAsIdentifier()) && result.size() == 1) { - auto type = resolve(result.front()); - result.clear(); - return lookup(result, type, II); - } else { - NNS->dump(); - std::terminate(); - } - } - - // prefix is a type, e.g. <...>::typename name:: - case clang::NestedNameSpecifier::SpecifierKind::TypeSpec: { - return lookup(result, clang::QualType(NNS->getAsType(), 0), II); - } - case clang::NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate: { - return lookup(result, clang::QualType(NNS->getAsType(), 0), II); - } - - default: { - NNS->dump(); - std::terminate(); - } - } -} - -bool DependentNameResolver::lookup(llvm::SmallVector& result, - const clang::QualType type, - const clang::IdentifierInfo* II) { - if(auto DNT = type->getAs()) { - return lookup(result, resolve(DNT), II); - } - - clang::TemplateDecl* TD; - std::vector args; - - // FIXME: consider default arguments - if(auto TST = type->getAs()) { - TD = TST->getTemplateName().getAsTemplateDecl(); - args = resugar(TST->template_arguments()); - } else if(auto DTST = type->getAs()) { - if(lookup(result, DTST->getQualifier(), DTST->getIdentifier()) && result.size() == 1) { - TD = llvm::dyn_cast(result.front()); - args = resugar(DTST->template_arguments()); - result.clear(); - } else { - return false; - } - } - - if(auto CTD = llvm::dyn_cast(TD)) { - return lookup(result, CTD, II, args); - } else if(auto TATD = llvm::dyn_cast(TD)) { - frames.emplace_back(TATD, args); - return lookup(result, resolve(substitute(TATD->getTemplatedDecl()->getUnderlyingType())), II); - } - - type->dump(); - llvm::outs() << II->getName() << "\n"; - std::terminate(); -} - -bool DependentNameResolver::lookup(llvm::SmallVector& result, - clang::ClassTemplateDecl* CTD, - const clang::IdentifierInfo* II, - llvm::ArrayRef arguments) { - - // main template first - auto decls = CTD->getTemplatedDecl()->lookup(II); - if(!decls.empty()) { - std::size_t count = 0; - for(auto decl: decls) { - result.push_back(decl); - } - - frames.emplace_back(CTD, arguments); - return true; - } - - for(auto base: CTD->getTemplatedDecl()->bases()) { - // store the current state - auto copy = frames; - - // try to find the member in the base class - frames.emplace_back(CTD, arguments); - if(lookup(result, substitute(base.getType()), II)) { - return true; - } - - // if failed, restore the state - frames = std::move(copy); - } - - // if failed, try partial specializations - llvm::SmallVector partials; - CTD->getPartialSpecializations(partials); - - for(auto partial: partials) { - clang::sema::TemplateDeductionInfo info(partial->getLocation()); - if(sema.DeduceTemplateArguments(partial, arguments, info) != clang::TemplateDeductionResult::Success) { - break; - } - - auto decls = partial->lookup(II); - if(decls.empty()) { - break; - } - - for(auto decl: decls) { - result.push_back(decl); - } - - // NOTE: takeSugared will take the ownership of the list - auto list = info.takeSugared(); - frames.emplace_back(partial, list->asArray()); - // FIXME: should we delete the list? - // delete list; - return true; - } - - return false; -} - -/// FIXME: handle more cese -std::vector DependentNameResolver::resugar(llvm::ArrayRef arguments) { - std::vector result; - for(auto arg: arguments) { - if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) { - // check whether it is a TemplateTypeParmType. - if(auto type = llvm::dyn_cast(arg.getAsType())) { - const clang::TemplateTypeParmDecl* param = type->getDecl(); - if(param && param->hasDefaultArgument()) { - result.push_back(param->getDefaultArgument().getArgument()); - } else { - result.emplace_back(resolve(arg.getAsType())); - } - continue; - } - } - result.push_back(arg); - } - return result; -} - -// FIXME: handle more case -static bool isalias(clang::QualType type) { - if(!type->isDependentType()) { - return false; - } - - if(auto TAT = type->getAs()) { - return true; - } else if(auto DNT = type->getAs()) { - for(auto arg: DNT->template_arguments()) { - if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) { - if(isalias(arg.getAsType())) { - return true; - } - } - } - return false; - } else if(auto DNT = type->getAs()) { - return isalias(clang::QualType(DNT->getQualifier()->getAsType(), 0)); - } else if(auto DTST = type->getAs()) { - return isalias(clang::QualType(DTST->getQualifier()->getAsType(), 0)); - } else if(auto LVRT = type->getAs()) { - return isalias(LVRT->getPointeeType()); - } else if(auto TTPT = type->getAs()) { - return false; - } else { - return false; - } -} - -clang::QualType DependentNameResolver::dealias(clang::QualType type) { - if(!isalias(type)) { - return type; - } - - if(auto TAT = type->getAs()) { - return dealias(TAT->getDecl()->getUnderlyingType()); - } else if(auto DNT = type->getAs()) { - llvm::SmallVector args; - for(auto arg: DNT->template_arguments()) { - if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) { - args.push_back(dealias(arg.getAsType())); - } else { - args.push_back(arg); - } - } - return context.getTemplateSpecializationType(DNT->getTemplateName(), args); - } - - else if(auto DNT = type->getAs()) { - auto type = dealias(clang::QualType(DNT->getQualifier()->getAsType(), 0)); - auto prefix = clang::NestedNameSpecifier::Create(context, nullptr, false, type.getTypePtr()); - return context.getDependentNameType(DNT->getKeyword(), prefix, DNT->getIdentifier()); - } else if(auto DTST = type->getAs()) { - auto type = dealias(clang::QualType(DTST->getQualifier()->getAsType(), 0)); - auto NNS = clang::NestedNameSpecifier::Create(context, nullptr, false, type.getTypePtr()); - auto keyword = DTST->getKeyword(); - auto identifier = DTST->getIdentifier(); - return context.getDependentTemplateSpecializationType(keyword, NNS, identifier, DTST->template_arguments()); - } else if(auto LVRT = type->getAs()) { - return context.getLValueReferenceType(dealias(LVRT->getPointeeType())); - } else { - return type; - } -} - -clang::QualType DependentNameResolver::substitute(clang::QualType type) { - clang::MultiLevelTemplateArgumentList list; - for(auto begin = frames.rbegin(), end = frames.rend(); begin != end; ++begin) { - list.addOuterTemplateArguments(begin->decl, begin->arguments, true); - } - - for(auto frame: frames) { - clang::Sema::CodeSynthesisContext context; - context.Entity = frame.decl; - context.TemplateArgs = frame.arguments.data(); - context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation; - sema.pushCodeSynthesisContext(context); - } - - // type->dump(); - auto result = sema.SubstType(dealias(type), list, {}, {}); - // result->dump(); - // llvm::outs() << "\n--------------------------------------------------\n"; - frames.clear(); - - return result; -} - } // namespace clice diff --git a/tests/AST/Resolver.cpp b/tests/AST/Resolver.cpp index ef3acdb6..41f83ded 100644 --- a/tests/AST/Resolver.cpp +++ b/tests/AST/Resolver.cpp @@ -156,253 +156,219 @@ TEST(DependentNameResolver, alias_dependent_name) { )"; Visitor visitor(code); clang::QualType result = visitor.test(); - result->dump(); match(result, "type_list", {"X"}); } -// -// TEST(DependentNameResolver, alias_template_dependent_name) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A { -// using type = T1; -// }; -// -// template -// struct B { -// using base = A; -// using type = type_list; -// }; -// -// template -// struct test { -// using result = typename B::type; -// }; -//)"; -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// -// auto TST = result->getAs(); -// ASSERT_TRUE(TST); -// ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); -// -// auto args = TST->template_arguments(); -// ASSERT_EQ(args.size(), 1); -// -// auto T = llvm::dyn_cast(args[0].getAsType()); -// ASSERT_TRUE(T); -// ASSERT_EQ(T->getDecl()->getName(), "X"); -// } -// -// TEST(DependentNameResolver, template_alias_dependent_name) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A { -// using type = type_list; -// }; -// -// template -// using B = A; -// -// template -// struct test { -// using result = typename B::type; -// }; -//)"; -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// // result->dump(); -// auto TST = result->getAs(); -// ASSERT_TRUE(TST); -// ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); -// -// auto args = TST->template_arguments(); -// ASSERT_EQ(args.size(), 1); -// -// auto T = llvm::dyn_cast(args[0].getAsType()); -// ASSERT_TRUE(T); -// ASSERT_EQ(T->getDecl()->getName(), "X"); -// } -// -// TEST(DependentNameResolver, dependent_member_template) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A { -// using type = type_list; -// }; -// -// template -// struct B { -// template -// using type = typename A::type; -// }; -// -// template -// struct test { -// using result = typename B::template type; -// }; -//)"; -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// auto TST = result->getAs(); -// ASSERT_TRUE(TST); -// ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); -// -// auto args = TST->template_arguments(); -// ASSERT_EQ(args.size(), 2); -// -// auto X = llvm::dyn_cast(args[0].getAsType()); -// ASSERT_TRUE(X); -// ASSERT_EQ(X->getDecl()->getName(), "X"); -// -// auto Y = llvm::dyn_cast(args[1].getAsType()); -// ASSERT_TRUE(Y); -// ASSERT_EQ(Y->getDecl()->getName(), "Y"); -// } -// -// TEST(DependentNameResolver, dependent_member_class_template) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A { -// template -// struct B { -// using type = type_list; -// }; -// }; -// -// template -// struct test { -// using result = typename A::template B::type; -// }; -//)"; -// -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// -// auto TST = result->getAs(); -// ASSERT_TRUE(TST); -// ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); -// -// auto args = TST->template_arguments(); -// ASSERT_EQ(args.size(), 2); -// -// auto X = llvm::dyn_cast(args[0].getAsType()); -// ASSERT_TRUE(X); -// ASSERT_EQ(X->getDecl()->getName(), "X"); -// -// auto Y = llvm::dyn_cast(args[1].getAsType()); -// ASSERT_TRUE(Y); -// ASSERT_EQ(Y->getDecl()->getName(), "Y"); -// } -// -// TEST(DependentNameResolver, dependent_partial_name) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A {}; -// -// template -// struct B {}; -// -// template typename HKT> -// struct B> { -// using type = type_list; -// }; -// -// template -// struct test { -// using result = typename B>::type; -// }; -//)"; -// -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// -// // FIXME: resugar the result -// } -// -// TEST(DependentNameResolver, dependent_base_name) { -// const char* code = R"( -// template -// struct type_list {}; -// -// template -// struct A { -// using type = type_list; -// }; -// -// template -// struct B : A {}; -// -// template -// struct test { -// using result = typename B::type; -// }; -//)"; -// -// Visitor visitor(code); -// auto result = visitor.test(); -// -// auto TST = result->getAs(); -// ASSERT_TRUE(TST); -// ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); -// -// auto args = TST->template_arguments(); -// ASSERT_EQ(args.size(), 1); -// -// auto T = llvm::dyn_cast(args[0].getAsType()); -// ASSERT_TRUE(T); -// ASSERT_EQ(T->getDecl()->getName(), "X"); -// } -// -// TEST(DependentNameResolver, std_vector) { -// const char* code = R"( -// #include -// -// template -// struct A {}; -// -// template -// struct test { -// using result = typename std::vector>::reference; -// }; -//)"; -// -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// // result->dump(); -// } -// -// TEST(DependentNameResolver, std_list) { -// const char* code = R"( -// #include -// -// template -// struct A {}; -// -// template -// struct test { -// using result = typename std::list>::reference; -// }; -//)"; -// -// Visitor visitor(code); -// clang::QualType result = visitor.test(); -// } +TEST(DependentNameResolver, alias_template_dependent_name) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + using type = T1; + }; + + template + struct B { + using base = A; + using type = type_list; + }; + + template + struct test { + using result = typename B::type; + }; +)"; + Visitor visitor(code); + clang::QualType result = visitor.test(); + match(result, "type_list", {"X"}); +} + +TEST(DependentNameResolver, template_alias_dependent_name) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + using type = type_list; + }; + + template + using B = A; + + template + struct test { + using result = typename B::type; + }; +)"; + Visitor visitor(code); + clang::QualType result = visitor.test(); + match(result, "type_list", {"X"}); +} + +TEST(DependentNameResolver, dependent_member_template) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + using type = type_list; + }; + + template + struct B { + template + using type = typename A::type; + }; + + template + struct test { + using result = typename B::template type; + }; +)"; + Visitor visitor(code); + clang::QualType result = visitor.test(); + match(result, "type_list", {"X", "Y"}); +} + +TEST(DependentNameResolver, dependent_member_class_template) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + template + struct B { + using type = type_list; + }; + }; + + template + struct test { + using result = typename A::template B::type; + }; +)"; + + Visitor visitor(code); + clang::QualType result = visitor.test(); + match(result, "type_list", {"X", "Y"}); +} + +TEST(DependentNameResolver, dependent_partial_name) { + const char* code = R"( + template + struct type_list {}; + + template + struct A {}; + + template + struct B {}; + + template typename HKT> + struct B> { + using type = type_list; + }; + + template + struct test { + using result = typename B>::type; + }; +)"; + + Visitor visitor(code); + clang::QualType result = visitor.test(); + result.dump(); +} + +TEST(DependentNameResolver, dependent_base_name) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + using type = type_list; + }; + + template + struct B : A {}; + + template + struct test { + using result = typename B::type; + }; +)"; + + Visitor visitor(code); + auto result = visitor.test(); + + match(result, "type_list", {"X"}); +} + +TEST(DependentNameResolver, dependent_base_name_2) { + const char* code = R"( + template + struct type_list {}; + + template + struct A { + using type = type_list; + }; + + template + struct test { + using base = typename A::type; + using result = base; + }; +)"; + + Visitor visitor(code); + auto result = visitor.test(); + llvm::outs() << "------------------------------------------\n"; + result.dump(); + match(result, "type_list", {"X"}); +} + +TEST(DependentNameResolver, std_vector) { + const char* code = R"( + #include + + template + struct A {}; + + template + struct test { + using result = typename std::vector::reference; + }; +)"; + + Visitor visitor(code); + clang::QualType result = visitor.test(); + llvm::outs() << "result is: { " << result.getAsString() << " }\n"; +} + +TEST(DependentNameResolver, std_list) { + const char* code = R"( + #include + + template + struct A {}; + + template + struct test { + using result = typename std::list>::reference; + }; +)"; + + Visitor visitor(code); + clang::QualType result = visitor.test(); + llvm::outs() << "result is: { " << result.getAsString() << " }\n"; +} } // namespace