#include #include class DependentShaper { clang::ASTContext& context; clang::Sema& sema; public: DependentShaper(clang::ASTContext& context, clang::Sema& sema) : context(context), sema(sema) {} /// replace template arguments in a dependent type /// e.g. /// ```cpp /// template /// struct A { /// using reference = T1&; /// }; /// /// template /// struct B { /// using type = A::reference; /// }; /// ``` /// we want to simplify `A::reference` to `U1&`, then we need to call /// `replace(, )` to get `U1&`. clang::QualType replace(clang::QualType input, llvm::ArrayRef originalArguments) { assert(type->isDependentType() && "type is not dependent"); // if the input is still a dependent name type, we need to simplify it recursively while(auto type = llvm::dyn_cast(input)) { input = simplify(type); } clang::ElaboratedType* elaboratedType = nullptr; if(auto type = llvm::dyn_cast(input)) { auto pointee = type->getPointeeType(); while(auto elaboratedType = llvm::dyn_cast(pointee)) { pointee = elaboratedType->desugar(); } // get the position of the dependent type in the template parameter list // e.g `T1` in , the index is 0 if(auto paramType = llvm::dyn_cast(pointee)) { auto index = paramType->getIndex(); auto argument = originalArguments[index]; // create a new type that fit the replacement return context.getLValueReferenceType(argument.getAsType()); } else if(auto defType = llvm::dyn_cast(pointee)) { return replace(defType->getDecl()->getUnderlyingType(), originalArguments); } else { pointee->dump(); std::terminate(); } } // TODO: handle other kinds return input; } clang::NamedDecl* lookup(const clang::ClassTemplateDecl* classTemplateDecl, const clang::IdentifierInfo* identifier) { clang::CXXRecordDecl* recordDecl = classTemplateDecl->getTemplatedDecl(); auto result = recordDecl->lookup(identifier); return result.front(); } /// for a complex dependent type: `X<...>::name::name2::...::nameN`, we can resolve it recursively. /// so we only need to handle the `X<...>::name`, whose prefix is a template specialization type. clang::QualType simplify(const clang::TemplateSpecializationType* templateType, const clang::IdentifierInfo* identifier) { // X is a class template or a type alias template auto templateDecl = templateType->getTemplateName().getAsTemplateDecl(); if(auto classTemplateDecl = llvm::dyn_cast(templateDecl)) { // lookup the identifier in the record decl auto namedDecl = lookup(classTemplateDecl, identifier); if(auto decl = llvm::dyn_cast(namedDecl)) { return replace(decl->getUnderlyingType(), templateType->template_arguments()); } else if(auto decl = llvm::dyn_cast(namedDecl)) { return replace(decl->getUnderlyingType(), templateType->template_arguments()); } else { namedDecl->dump(); } } else if(auto aliasTemplateDecl = llvm::dyn_cast(templateDecl)) { // TODO: } else { templateDecl->dump(); } } const clang::QualType simplify(const clang::NestedNameSpecifier* specifier, const clang::IdentifierInfo* identifier) { auto kind = specifier->getKind(); switch(specifier->getKind()) { case clang::NestedNameSpecifier::Identifier: { const auto prefix = simplify(specifier->getPrefix(), specifier->getAsIdentifier()); if(auto type = llvm::dyn_cast(prefix)) { return simplify(type, identifier); } else { prefix->dump(); } break; } case clang::NestedNameSpecifier::TypeSpec: { auto node = specifier->getAsType(); if(auto type = llvm::dyn_cast(node)) { // represent a direct dependent name, e.g. typename T::^ name // and can not be further simplified // node->dump(); type->dump(); } else if(auto type = node->getAs()) { // represent a dependent name that is a template specialization // e.g. typename vector::^ name, and can be further simplified return simplify(type, identifier); } else { node->dump(); } break; } case clang::NestedNameSpecifier::TypeSpecWithTemplate: { llvm::outs() << "unsupported kind: " << kind << "\n"; break; } default: { llvm::outs() << "unsupported kind: " << kind << "\n"; } } } const clang::QualType simplify(const clang::DependentNameType* type) { // llvm::outs() << "-----------------------------------------------------------" << "\n"; // type->dump(); return simplify(type->getQualifier(), type->getIdentifier()); } }; class ASTVistor : public clang::RecursiveASTVisitor { private: clang::Preprocessor& preprocessor; clang::SourceManager& sourceManager; clang::syntax::TokenBuffer& buffer; clang::ASTContext& context; clang::Sema& sema; public: ASTVistor(clang::Preprocessor& preprocessor, clang::syntax::TokenBuffer& buffer, clang::ASTContext& context, clang::Sema& sema) : preprocessor(preprocessor), sourceManager(preprocessor.getSourceManager()), buffer(buffer), context(context), sema(sema) {} bool VisitTypeAliasDecl(clang::TypeAliasDecl* decl) { auto& sm = context.getSourceManager(); if(sm.isInMainFile(decl->getLocation()) && decl->getName() == "type") { auto type = decl->getUnderlyingType(); type.dump(); llvm::outs() << "----------------------------------------------------------------\n"; if(auto templateType = type->getAs()) { DependentShaper shaper{context}; auto result = shaper.simplify(templateType); result->dump(); } } return true; } }; int main(int argc, const char** argv) { assert(argc == 2 && "Usage: Preprocessor "); llvm::outs() << "running ASTVisitor...\n"; auto instance = std::make_unique(); clang::DiagnosticIDs* ids = new clang::DiagnosticIDs(); clang::DiagnosticOptions* diag_opts = new clang::DiagnosticOptions(); clang::DiagnosticConsumer* consumer = new clang::IgnoringDiagConsumer(); clang::DiagnosticsEngine* engine = new clang::DiagnosticsEngine(ids, diag_opts, consumer); instance->setDiagnostics(engine); auto invocation = std::make_shared(); std::vector args = { "/home/ykiko/Project/C++/clice/external/llvm/bin/clang++", "-Xclang", "-no-round-trip-args", "-std=c++20", argv[1], }; invocation = clang::createInvocation(args, {}); // clang::CompilerInvocation::CreateFromArgs(*invocation, args, instance->getDiagnostics()); instance->setInvocation(std::move(invocation)); if(!instance->createTarget()) { llvm::errs() << "Failed to create target\n"; std::terminate(); } clang::SyntaxOnlyAction action; if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } clang::syntax::TokenCollector collector{instance->getPreprocessor()}; if(auto error = action.Execute()) { llvm::errs() << "Failed to execute action: " << error << "\n"; std::terminate(); } clang::syntax::TokenBuffer buffer = std::move(collector).consume(); auto tu = instance->getASTContext().getTranslationUnitDecl(); ASTVistor visitor{instance->getPreprocessor(), buffer, instance->getASTContext(), instance->getSema()}; visitor.TraverseDecl(tu); action.EndSourceFile(); };