From c975cbf6837e3e63bb7068cd772693010bc2a0e2 Mon Sep 17 00:00:00 2001 From: ykiko Date: Sun, 28 Jul 2024 10:24:57 +0800 Subject: [PATCH] some update. --- .vscode/launch.json | 2 +- CMakeLists.txt | 2 +- docs/clang/README.md | 14 ++- docs/examples/ASTVisitor.cpp | 185 +++++++++++++++++++++++++++++--- include/Clang/DependentShaper.h | 65 +++++++++++ main.cpp | 23 ++-- src/Feature/SemanticToken.cpp | 50 +++++++-- 7 files changed, 299 insertions(+), 42 deletions(-) create mode 100644 include/Clang/DependentShaper.h diff --git a/.vscode/launch.json b/.vscode/launch.json index 30e8dbee..d91ebaae 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,7 @@ "type": "lldb", "request": "launch", "name": "Launch", - "program": "${workspaceFolder}/build/Preamble", + "program": "${workspaceFolder}/build/ASTVisitor", "args": [ "/home/ykiko/Project/C++/clice/main.cpp", ], diff --git a/CMakeLists.txt b/CMakeLists.txt index a669603e..e7821c6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(clice) set(CMAKE_CXX_STANDARD 23) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 -fno-rtti") +set(CMAKE_CXX_FLAGS "-isystem/usr/include/c++/13 -isystem/usr/include/x86_64-linux-gnu/c++/13 ${CMAKE_CXX_FLAGS_RELEASE} -g -O0 -fno-rtti") set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/external/llvm/lib/cmake") find_package(LLVM REQUIRED CONFIG) diff --git a/docs/clang/README.md b/docs/clang/README.md index fcc13ba8..8b7223dc 100644 --- a/docs/clang/README.md +++ b/docs/clang/README.md @@ -179,13 +179,21 @@ public: preprocessor.addPPCallbacks(std::make_unique()); ``` -# AST + + +This class is used to simplify a dependent type, so that we can do more things on it. e.g. code +completion. Dependent type is a type that depends on a template parameter. + +For example: ```cpp - +template +void foo(std::vector vec); ``` - +`std::vector` is a dependent type, because it depends on the template parameter `T`. Beacuse it has not been instantiated yet, we can't determine which specialization it will use(if have), even can't not determine whether the instantiation is valid or not. Beacuse of this, we can't not provide **precise** code completion for it. + +Note that I emphasis the word **precise**. In fact, in most of the cases, not very precise is tolerable. It's better than nothing. We can only consider the main template and provide code completion for it. diff --git a/docs/examples/ASTVisitor.cpp b/docs/examples/ASTVisitor.cpp index a76109f0..1e7035b1 100644 --- a/docs/examples/ASTVisitor.cpp +++ b/docs/examples/ASTVisitor.cpp @@ -1,28 +1,181 @@ #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) : - preprocessor(preprocessor), sourceManager(preprocessor.getSourceManager()), buffer(buffer) {} + 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 VisitDecl(clang::Decl* decl) { - if(clang::NamedDecl* named = llvm::dyn_cast(decl)) { - auto name = named->getName(); - if(name == "main") { - llvm::outs() << "Found main function\n"; - auto location = named->getLocation(); - llvm::outs() << "line: " << sourceManager.getPresumedLineNumber(location) << "\n"; - llvm::outs() << "column: " << sourceManager.getPresumedColumnNumber(location) << "\n"; - llvm::outs() << "line: " << sourceManager.getSpellingLineNumber(location) << "\n"; - llvm::outs() << "column: " << sourceManager.getSpellingColumnNumber(location) << "\n"; - // auto token = buffer.spelledTokenAt(location); + 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; } }; @@ -35,7 +188,7 @@ int main(int argc, const char** argv) { clang::DiagnosticIDs* ids = new clang::DiagnosticIDs(); clang::DiagnosticOptions* diag_opts = new clang::DiagnosticOptions(); - clang::DiagnosticConsumer* consumer = new clang::TextDiagnosticPrinter(llvm::errs(), diag_opts); + clang::DiagnosticConsumer* consumer = new clang::IgnoringDiagConsumer(); clang::DiagnosticsEngine* engine = new clang::DiagnosticsEngine(ids, diag_opts, consumer); instance->setDiagnostics(engine); @@ -74,8 +227,8 @@ int main(int argc, const char** argv) { clang::syntax::TokenBuffer buffer = std::move(collector).consume(); auto tu = instance->getASTContext().getTranslationUnitDecl(); - ASTVistor visitor{instance->getPreprocessor(), buffer}; + ASTVistor visitor{instance->getPreprocessor(), buffer, instance->getASTContext(), instance->getSema()}; visitor.TraverseDecl(tu); action.EndSourceFile(); -} +}; diff --git a/include/Clang/DependentShaper.h b/include/Clang/DependentShaper.h new file mode 100644 index 00000000..d1c34736 --- /dev/null +++ b/include/Clang/DependentShaper.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +namespace clice { + +class DependentShaper { + clang::ASTContext& context; + +public: + DependentShaper(clang::ASTContext& context) : context(context) {} + + const clang::NamedDecl* simplify(const clang::TemplateSpecializationType* templateType, + const clang::IdentifierInfo* identifier) { + // get the template decl of the template specialization + // e.g. vector -> vector + + // get the template arguments of the template specialization + // e.g. vector -> int + auto templateDecl = templateType->getTemplateName().getAsTemplateDecl(); + if(auto decl = llvm::dyn_cast(templateDecl)) { + // get the record decl of the template decl + clang::CXXRecordDecl* recordDecl = decl->getTemplatedDecl(); + // lookup the identifier in the record decl + auto result = recordDecl->lookup(identifier); + assert(result.size() == 1 && "multiple results found"); + return result.front(); + + } else if(auto decl = llvm::dyn_cast(templateDecl)) { + } + } + + const clang::NamedDecl* simplify(const clang::NestedNameSpecifier* specifier, + const clang::IdentifierInfo* identifier) { + switch(specifier->getKind()) { + case clang::NestedNameSpecifier::TypeSpec: { + auto node = specifier->getAsType(); + + if(auto type = node->getAs()) { + // represent a direct dependent name, e.g. typename T::^ name + // and can not be further simplified + } 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); + } + + break; + } + + case clang::NestedNameSpecifier::TypeSpecWithTemplate: { + break; + } + + default: { + } + } + } + + const clang::NamedDecl* simplify(const clang::DependentNameType* type) { + return simplify(type->getQualifier(), type->getIdentifier()); + } +}; + +} // namespace clice diff --git a/main.cpp b/main.cpp index 7df86ada..799e58d2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,13 +1,14 @@ -#include +template +struct A { + using reference = T&; +}; -#define X(x) \ - int x; \ - int y; \ - int z; +template +struct B { + using reference = A::reference; +}; -X(x) - -int main() { - std::cout << x << std::endl; - return 0; -} +template +struct C { + using type = B::reference; +}; diff --git a/src/Feature/SemanticToken.cpp b/src/Feature/SemanticToken.cpp index d2b1c422..e88ea0f9 100644 --- a/src/Feature/SemanticToken.cpp +++ b/src/Feature/SemanticToken.cpp @@ -9,7 +9,17 @@ using SemanticTokenModifiers = protocol::SemanticTokenModifiers; struct SemanticToken { clang::syntax::FileRange range; SemanticTokenTypes type; - uint32_t modifiers; + uint32_t modifiers = 0; + + SemanticToken& setType(SemanticTokenTypes type) { + this->type = type; + return *this; + } + + SemanticToken& addModifier(SemanticTokenModifiers modifier) { + modifiers |= modifier; + return *this; + } }; class HighlighCollector { @@ -17,8 +27,13 @@ private: clang::ASTContext& context; clang::syntax::TokenBuffer& buffer; clang::SourceManager& sourceManager; + // store all tokens in the main file std::vector tokens; llvm::DenseMap tokenMap; + // cache the last location and token index + // to have a better performance in traversing the same node + clang::SourceLocation lastLocation; + std::optional lastTokenIndex; private: /// determine whether the token is a keyword @@ -88,19 +103,30 @@ public: void attach(clang::Decl* decl, SemanticTokenTypes type, uint32_t modifier); void attach(clang::SourceLocation location, SemanticTokenTypes type, uint32_t modifier) { - auto token = buffer.spelledTokenAt(location); - if(token) { - auto iter = tokenMap.find(token); - if(iter != tokenMap.end()) { + // check whether the location is same as the last location + if(lastLocation != location) { + // if not, update the last location and find the token + lastLocation = location; + auto token = buffer.spelledTokenAt(location); + if(token) { + // if there is a corresponding token, update the token + auto iter = tokenMap.find(token); + assert(iter != tokenMap.end()); auto index = iter->second; tokens[index].type = type; - tokens[index].modifiers = modifier; + tokens[index].modifiers |= modifier; + lastTokenIndex = index; + } else { + // if there is no corresponding token, reset the last token index + lastTokenIndex.reset(); + } + } else { + // if yes, use the last token index to find the token + if(lastTokenIndex) { + tokens[*lastTokenIndex].type = type; + tokens[*lastTokenIndex].modifiers |= modifier; } } - - // buffer.expandedForSpelled() - // buffer.spelledForExpanded(); - // buffer.spelledTokenContaining(); } void collect() {} @@ -235,6 +261,10 @@ public: VISIT(Expr) { return collector.isInMainFile(node->getBeginLoc()); } + VISIT(DeclRefExpr) {} + + VISIT(MemberExpr) {} + VISIT(Attr) { auto location = node->getLocation(); if(collector.isInMainFile(location)) {