diff --git a/CMakeLists.txt b/CMakeLists.txt index ca3666eb..6c2515be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(CLICE) set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -w -fno-rtti -g -O0") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti -g -O0") set(LLVM_INSTALL_PATH "${CMAKE_SOURCE_DIR}/deps/llvm/build-install") diff --git a/docs/semantic-tokens.md b/docs/semantic-tokens.md new file mode 100644 index 00000000..e69de29b diff --git a/include/AST/Directive.h b/include/AST/Directive.h index 9f9d6028..52fe57e9 100644 --- a/include/AST/Directive.h +++ b/include/AST/Directive.h @@ -5,9 +5,10 @@ namespace clice { // TODO: struct Directive { + clang::SourceManager& sourceManager; llvm::StringSet<> includes; - std::vector comments; - + llvm::StringMap> comments; + clang::CommentHandler* handler(); std::unique_ptr callback(); }; diff --git a/include/Protocol/SemanticTokens.def b/include/Protocol/SemanticTokens.def index 2fe042e8..a48926fa 100644 --- a/include/Protocol/SemanticTokens.def +++ b/include/Protocol/SemanticTokens.def @@ -72,17 +72,6 @@ SEMANTIC_TOKEN_TYPE(Method, "method") /// C/C++ function/method parameter name. SEMANTIC_TOKEN_TYPE(Parameter, "parameter") -/// C++ dependent name in a template context (e.g., `name` in `auto y = T::name` -/// where T is a template parameter). -/// Note: This includes `T::template name(...)`; it's not possible to distinguish whether a name -/// is a function or a functor in a dependent context. -SEMANTIC_TOKEN_TYPE(DependentName, "dependentName") - -/// C++ dependent type name (e.g., `type` in `typename std::vector::type` where -/// T is a template parameter). -/// Note: This includes `typename T::template type<...>`. -SEMANTIC_TOKEN_TYPE(DependentType, "dependentType") - /// C++ attribute name(e.g., `nodiscard`, `likely`, `fallthrough`). SEMANTIC_TOKEN_TYPE(Attribute, "attribute") @@ -124,3 +113,19 @@ SEMANTIC_TOKEN_MODIFIER(InType, "inType") /// for overloaded function or operator. SEMANTIC_TOKEN_MODIFIER(Overloaded, "overloaded") + +/// for name around template arguments(e.g., `foo` in `foo`). +SEMANTIC_TOKEN_MODIFIER(Templated, "templated") + +/// C++ dependent name in a template context (e.g., `name` in `auto y = T::name` +/// where T is a template parameter). +/// Note: This includes `T::template name(...)`; it's not possible to distinguish whether a name +/// is a function or a functor in a dependent context. + +/// C++ dependent type name (e.g., `type` in `typename std::vector::type` where +/// T is a template parameter). +/// Note: This includes `typename T::template type<...>`. +SEMANTIC_TOKEN_MODIFIER(Dependent, "dependent") + +#undef SEMANTIC_TOKEN_TYPE +#undef SEMANTIC_TOKEN_MODIFIER \ No newline at end of file diff --git a/include/Protocol/SemanticTokens.h b/include/Protocol/SemanticTokens.h index 4539c416..c83ed42d 100644 --- a/include/Protocol/SemanticTokens.h +++ b/include/Protocol/SemanticTokens.h @@ -23,13 +23,11 @@ struct SemanticTokensLegend { std::array tokenTypes = { #define SEMANTIC_TOKEN_TYPE(name, value) value, #include "SemanticTokens.def" - #undef SEMANTIC_TOKEN_TYPE }; std::array tokenModifiers = { #define SEMANTIC_TOKEN_MODIFIER(name, value) value, #include "SemanticTokens.def" - #undef SEMANTIC_TOKEN_MODIFIER }; }; // clang-format on diff --git a/include/Support/Reflection.h b/include/Support/Reflection.h index d3e01b1b..00e6a6f6 100644 --- a/include/Support/Reflection.h +++ b/include/Support/Reflection.h @@ -1,6 +1,7 @@ #pragma once // support basic reflection through template meta programming +#include #include #include #include @@ -38,7 +39,7 @@ struct Wrapper { }; template -constexpr auto member_name() { +consteval auto member_name() { std::string_view name = std::source_location::current().function_name(); #if __GNUC__ && (!__clang__) && (!_MSC_VER) std::size_t start = name.rfind("::") + 2; @@ -163,6 +164,32 @@ struct Storage { inline static T value; }; +template +consteval auto enum_name() { + std::string_view name = std::source_location::current().function_name(); +#if __GNUC__ || __clang__ + std::size_t start = name.find('=') + 2; + std::size_t end = name.size() - 1; +#elif _MSC_VER + std::size_t start = name.find('<') + 1; + std::size_t end = name.rfind(">("); +#else + static_assert(false, "Not supported compiler"); +#endif + name = name.substr(start, end - start); + start = name.rfind("::"); + return start == std::string_view::npos ? name : name.substr(start + 2); +} + +template +consteval auto enum_max() { + constexpr auto value = std::bit_cast(static_cast>(N)); + if constexpr(enum_name().find(")") == std::string_view::npos) + return enum_max(); + else + return N; +} + template struct replace_cv_ref; @@ -234,4 +261,14 @@ struct Record : Ts... { } }; +template + requires std::is_enum_v +constexpr auto enum_name(T value) { + constexpr auto count = impl::enum_max(); + constexpr auto names = [](std::index_sequence) { + return std::array{impl::enum_name(Is)>()...}; + }(std::make_index_sequence{}); + return names[static_cast(value)]; +} + }; // namespace clice diff --git a/src/AST/Directive.cpp b/src/AST/Directive.cpp index be97dd48..2cacb381 100644 --- a/src/AST/Directive.cpp +++ b/src/AST/Directive.cpp @@ -1,5 +1,5 @@ #include "AST/Directive.h" - +#include namespace clice { struct CommentHandler : public clang::CommentHandler { @@ -8,40 +8,87 @@ struct CommentHandler : public clang::CommentHandler { CommentHandler(Directive& directive) : directive(directive) {} virtual bool HandleComment(clang::Preprocessor& preproc, clang::SourceRange Comment) { - directive.comments.push_back(Comment); + // directive.comments.push_back(Comment); return false; } }; struct PPCallback : clang::PPCallbacks { - virtual void InclusionDirective(clang::SourceLocation HashLoc, - const clang::Token& IncludeTok, - llvm::StringRef FileName, - bool IsAngled, - clang::CharSourceRange FilenameRange, - clang::OptionalFileEntryRef File, - clang::StringRef SearchPath, - llvm::StringRef RelativePath, - const clang::Module* SuggestedModule, - bool ModuleImported, - clang::SrcMgr::CharacteristicKind FileType) { + PPCallback(Directive& directive) : directive(directive) {} + + Directive& directive; + + void FileChanged(clang::SourceLocation loc, + clang::PPCallbacks::FileChangeReason reason, + clang::SrcMgr::CharacteristicKind fileType, + clang::FileID id) override { + llvm::outs() << "FileChanged, reason: " << enum_name(reason) << "\n"; + loc.dump(directive.sourceManager); + } + + void InclusionDirective(clang::SourceLocation HashLoc, + const clang::Token& IncludeTok, + llvm::StringRef FileName, + bool IsAngled, + clang::CharSourceRange FilenameRange, + clang::OptionalFileEntryRef File, + clang::StringRef SearchPath, + llvm::StringRef RelativePath, + const clang::Module* SuggestedModule, + bool ModuleImported, + clang::SrcMgr::CharacteristicKind FileType) override { // TODO: record all include files - namespace fs = llvm::sys::fs; - namespace path = llvm::sys::path; llvm::SmallVector RealPath; // fs::make_absolute(SearchPath + "/" + RelativePath, RealPath); // path::remove_dots(RealPath, /*remove_dot_dot=*/true); // llvm::outs() << RealPath << "\n"; } - // virtual void - // moduleImport(clang::SourceLocation ImportLoc, clang::ModuleIdPath Path, const clang::Module* Imported) { - // // store for highlight - // } + void PragmaDirective(clang::SourceLocation Loc, clang::PragmaIntroducerKind Introducer) override { + // llvm::outs() << "PragmaDirective\n"; + } + + void If(clang::SourceLocation Loc, + clang::SourceRange ConditionRange, + clang::PPCallbacks::ConditionValueKind ConditionValue) override { + // llvm::outs() << "If\n"; + } + + void Elif(clang::SourceLocation loc, + clang::SourceRange conditionRange, + clang::PPCallbacks::ConditionValueKind conditionValue, + clang::SourceLocation ifLoc) override {} + + void Ifdef(clang::SourceLocation loc, const clang::Token& name, const clang::MacroDefinition& definition) override { + } + + void Elifdef(clang::SourceLocation loc, + const clang::Token& name, + const clang::MacroDefinition& definition) override {} + + void Elifdef(clang::SourceLocation loc, clang::SourceRange conditionRange, clang::SourceLocation ifLoc) override {} + + void Ifndef(clang::SourceLocation loc, + const clang::Token& name, + const clang::MacroDefinition& definition) override {} + + // invoke when #elifndef is taken + void Elifndef(clang::SourceLocation loc, + const clang::Token& name, + const clang::MacroDefinition& definition) override {} + + // invoke when #elifndef is skipped + void Elifndef(clang::SourceLocation loc, clang::SourceRange conditionRange, clang::SourceLocation ifLoc) override {} + + void Else(clang::SourceLocation loc, clang::SourceLocation ifLoc) override {} + + void Endif(clang::SourceLocation loc, clang::SourceLocation ifLoc) override {} + + void MacroDefined(const clang::Token& MacroNameTok, const clang::MacroDirective* MD) override {} }; clang::CommentHandler* Directive::handler() { return new CommentHandler(*this); } -std::unique_ptr Directive::callback() { return std::make_unique(); } +std::unique_ptr Directive::callback() { return std::make_unique(*this); } } // namespace clice diff --git a/src/AST/ParsedAST.cpp b/src/AST/ParsedAST.cpp index 3de9ebe0..b25600c9 100644 --- a/src/AST/ParsedAST.cpp +++ b/src/AST/ParsedAST.cpp @@ -65,7 +65,7 @@ std::unique_ptr ParsedAST::build(llvm::StringRef filename, auto& preproc = instance->getPreprocessor(); clang::syntax::TokenCollector collector(preproc); - auto directive = std::make_unique(); + auto directive = std::make_unique(instance->getSourceManager()); preproc.addCommentHandler(directive->handler()); preproc.addPPCallbacks(directive->callback()); diff --git a/src/Feature/SemanticTokens.cpp b/src/Feature/SemanticTokens.cpp index 92337929..f0fc2e9b 100644 --- a/src/Feature/SemanticTokens.cpp +++ b/src/Feature/SemanticTokens.cpp @@ -160,6 +160,8 @@ static bool isKeyword(clang::tok::TokenKind kind, llvm::StringRef text, const cl case clang::tok::kw_co_return: { return option.CPlusPlus20; } + + default: break; } return false; } @@ -178,6 +180,7 @@ public: if(token) { return addToken(type, token->location(), token->length()); } + } // FIXME: source range can be a multi-line range, split it into multiple tokens @@ -191,7 +194,34 @@ public: // return result.back(); //} - void addAngle(clang::SourceLocation left, clang::SourceLocation right) {} + void addAngle(clang::SourceLocation left, clang::SourceLocation right) { + if(left.isInvalid() || right.isInvalid()) { + return; + } + + llvm::outs() << "is macro?: " << left.isMacroID() << " "; + left.dump(AST.sourceManager); + llvm::outs() << "is macro?: " << right.isMacroID() << " "; + right.dump(AST.sourceManager); + + if(auto token = AST.tokenBuffer.spelledTokenContaining(left)) { + addToken(protocol::SemanticTokenType::Angle, token->location(), token->length()) + .addModifier(protocol::SemanticTokenModifier::Left); + } + + // RLoc might be pointing at a virtual buffer when it's part of a `>>` token. + auto loc = AST.sourceManager.getFileLoc(right); + if(auto token = AST.tokenBuffer.spelledTokenContaining(loc)) { + if(token->kind() == clang::tok::greater) { + addToken(protocol::SemanticTokenType::Angle, loc, 1) + .addModifier(protocol::SemanticTokenModifier::Right); + } else if(token->kind() == clang::tok::greatergreater) { + // TODO: split `>>` into two tokens + addToken(protocol::SemanticTokenType::Angle, loc, 2) + .addModifier(protocol::SemanticTokenModifier::Right); + } + } + } std::vector build(); @@ -225,13 +255,20 @@ public: // WalkUpFrom(NamespaceDecl) {} - VISIT(ImportDecl) {} + VISIT(ImportDecl) { return true; } + + VISIT(NamedDecl) { return true; } VISIT(NamespaceDecl) { builder.addToken(protocol::SemanticTokenType::Namespace, node->getLocation()); return true; } + VISIT(VarDecl) { + builder.addToken(protocol::SemanticTokenType::Variable, node->getLocation()); + return true; + } + VISIT(DeclaratorDecl) { for(unsigned i = 0; i < node->getNumTemplateParameterLists(); ++i) { if(auto params = node->getTemplateParameterList(i)) { @@ -293,13 +330,30 @@ public: return true; } + VISIT(OverloadExpr) { + builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + return true; + } + + VISIT(DeclRefExpr) { + builder.addToken(protocol::SemanticTokenType::Variable, node->getLocation()); + node->getEnumConstantDecl(); + builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + return true; + } + VISIT(CXXNamedCastExpr) { builder.addAngle(node->getAngleBrackets().getBegin(), node->getAngleBrackets().getEnd()); return true; } - VISIT(OverloadExpr) { + VISIT(DependentScopeDeclRefExpr) { + // `T::value<...>` + // ^ ^^^^^~~~ Angles + // ^~~~ DependentValue builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + builder.addToken(protocol::SemanticTokenType::Variable, node->getLocation()) + .addModifier(protocol::SemanticTokenModifier::Dependent); return true; } @@ -308,21 +362,35 @@ public: return true; } - VISIT(DependentScopeDeclRefExpr) { - builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + VISIT_TYPE(RecordTypeLoc) { + // `struct X x;` + // ^ Type + builder.addToken(protocol::SemanticTokenType::Type, node.getNameLoc()); return true; } VISIT_TYPE(DependentNameTypeLoc) { - // DependentNameType: `typename T::type` - // ^~~~ highlight this + // `typename T::type` + // ^~~~ DependentType + builder.addToken(protocol::SemanticTokenType::Type, node.getNameLoc()) + .addModifier(protocol::SemanticTokenModifier::Dependent); + return true; + } + + VISIT_TYPE(TemplateTypeParmTypeLoc) { + // `typename T::type` + // ^~~~ Type builder.addToken(protocol::SemanticTokenType::Type, node.getNameLoc()); return true; } VISIT_TYPE(TemplateSpecializationTypeLoc) { - node.dump(); + // `Template<...>` + // ^ ^~~~ Angles + // ^~~~ Type builder.addAngle(node.getLAngleLoc(), node.getRAngleLoc()); + builder.addToken(protocol::SemanticTokenType::Type, node.getTemplateNameLoc()) + .addModifier(protocol::SemanticTokenModifier::Templated); return true; } @@ -334,9 +402,10 @@ public: bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc loc) { if(clang::NestedNameSpecifier* NNS = loc.getNestedNameSpecifier()) { if(NNS->getKind() == clang::NestedNameSpecifier::Identifier) { - // NestedNameSpecifier: `T::type::` - // ^~~~ highlight this - builder.addToken(protocol::SemanticTokenType::Type, loc.getLocalBeginLoc()); + // `T::type::` + // ^~~~ DependentType + builder.addToken(protocol::SemanticTokenType::Type, loc.getLocalBeginLoc()) + .addModifier(protocol::SemanticTokenModifier::Dependent); } } return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(loc); @@ -409,10 +478,6 @@ protocol::SemanticTokens semanticTokens(const ParsedAST& AST, llvm::StringRef fi HighlightBuilder builder(AST, filename); std::vector tokens = builder.build(); - // for(auto& token: tokens) { - // spdlog::info("{}", token.dump(AST.sourceManager)); - // } - protocol::SemanticTokens result; unsigned int last_line = 0; unsigned int last_column = 0; diff --git a/tests/Feature/SemanticTokens.cpp b/tests/Feature/SemanticTokens.cpp index 991a536b..137f5b61 100644 --- a/tests/Feature/SemanticTokens.cpp +++ b/tests/Feature/SemanticTokens.cpp @@ -12,33 +12,24 @@ auto f() { return T::value; } -std::vector compileArgs = { - "clang++", - "-std=c++20", - "main.cpp", - "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", -}; - TEST(test, test) { + std::vector compileArgs = { + "clang++", + "-std=c++20", + "main.cpp", + "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", + }; +#include + const char* code = R"( -template -struct X { - using type = T; - static constexpr bool value = true; -}; - -template -auto f() { - using type = typename X::type::type; - return T::value; -} +#include )"; auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs); auto fileID = AST->getFileID("main.cpp"); - AST->context.getTranslationUnitDecl()->dump(); - auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp"); + // AST->context.getTranslationUnitDecl()->dump(); + // auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp"); } } // namespace diff --git a/tests/test.cpp b/tests/test.cpp deleted file mode 100644 index d522f391..00000000 --- a/tests/test.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include - -namespace { - -using namespace clang; - -template -auto f() { - using type = typename T::type; - return T::value; -} - -TEST(test, test) { - std::vector compileArgs = { - "clang++", - "-std=c++20", - "main.cpp", - "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", - }; - - const char* code = R"( -template -struct X { - using type = T; - static constexpr bool value = true; -}; - -template -auto f() { - using type = typename X::type::type; - return T::value; -} -)"; - - auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs); - auto fileID = AST->getFileID("main.cpp"); - AST->context.getTranslationUnitDecl()->dump(); - auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp"); -} - -} // namespace -