From 2f9622bca77604b6017c11a770e719806fe033ed Mon Sep 17 00:00:00 2001 From: ykiko Date: Wed, 5 Feb 2025 14:10:11 +0800 Subject: [PATCH] Clean the project (#57) --- CMakeLists.txt | 3 +- include/{Basic => AST}/RelationKind.h | 0 include/{Compiler => AST}/Resolver.h | 37 +- include/{Compiler => AST}/Selection.h | 4 +- include/{Compiler => AST}/Semantic.h | 32 +- include/AST/SourceLocation.h | 35 + include/{Basic => AST}/SymbolKind.h | 0 include/{Compiler => AST}/Utility.h | 2 +- include/Async/Async.h | 2 +- include/Async/FileSystem.h | 12 +- include/Async/{Socket.h => Network.h} | 9 +- include/Async/libuv.h | 9 - include/Basic/HeaderContext.h | 34 - include/Basic/SourceCode.h | 4 +- include/Compiler/AST.h | 5 +- include/Compiler/Clang.h | 51 - include/Compiler/Command.h | 10 +- include/Compiler/Compilation.h | 9 +- include/Compiler/Diagnostic.h | 2 +- include/Compiler/Directive.h | 4 +- include/Compiler/Module.h | 6 +- include/Compiler/Preamble.h | 3 +- include/Compiler/TypePrinter.h | 0 include/Feature/SemanticTokens.h | 2 +- include/Index/SymbolIndex.h | 4 +- include/Index/USR.h | 15 + include/Server/Config.h | 5 +- include/Server/Indexer.h | 4 +- include/Support/ADT.h | 21 - include/Support/Compare.h | 3 +- include/Support/Enum.h | 1 + include/Support/Error.h | 18 - include/Support/Format.h | 2 +- include/{Server => Support}/Logger.h | 4 +- include/Support/Support.h | 12 - include/Test/CTest.h | 3 - include/Test/Test.h | 6 +- src/{Compiler => AST}/Resolver.cpp | 52 +- src/{Compiler => AST}/Selection.cpp | 65 +- src/{Basic => AST}/SymbolKind.cpp | 2 +- src/{Compiler => AST}/Utility.cpp | 14 +- src/Async/Async.cpp | 280 +----- src/Async/FileSystem.cpp | 4 +- src/Async/Network.cpp | 240 +++++ src/Basic/SourceCode.cpp | 1 + src/Basic/SourceConverter.cpp | 5 +- src/Compiler/Command.cpp | 12 +- src/Compiler/Compilation.cpp | 35 +- src/Compiler/Diagnostic.cpp | 55 +- src/Compiler/Directive.cpp | 8 +- src/Compiler/Module.cpp | 6 +- src/Driver/clice.cc | 6 +- src/Driver/integration_tests.cc | 18 +- src/Driver/unit_tests.cc | 3 +- src/Feature/CodeCompletion.cpp | 78 +- src/Feature/DocumentSymbol.cpp | 1 + src/Feature/FoldingRange.cpp | 1 + src/Feature/InlayHint.cpp | 1 + src/Feature/SemanticTokens.cpp | 3 +- src/Index/SymbolIndex.cpp | 4 +- src/Index/USRGeneration.cpp | 922 +++++++++++++++++- src/Server/Config.cpp | 8 +- src/Server/Database.cpp | 4 +- src/Server/Indexer.cpp | 6 +- src/Server/Lifecycle.cpp | 1 + src/Server/Server.cpp | 8 +- unittests/{Compiler => AST}/Resolver.cpp | 1 + .../{Compiler => AST}/SemanticVisitor.cpp | 2 +- unittests/Compiler/Command.cpp | 6 +- unittests/Index/USR.cpp | 37 + xmake.lua | 1 - 71 files changed, 1521 insertions(+), 742 deletions(-) rename include/{Basic => AST}/RelationKind.h (100%) rename include/{Compiler => AST}/Resolver.h (64%) rename include/{Compiler => AST}/Selection.h (88%) rename include/{Compiler => AST}/Semantic.h (97%) create mode 100644 include/AST/SourceLocation.h rename include/{Basic => AST}/SymbolKind.h (100%) rename include/{Compiler => AST}/Utility.h (96%) rename include/Async/{Socket.h => Network.h} (72%) delete mode 100644 include/Basic/HeaderContext.h delete mode 100644 include/Compiler/Clang.h delete mode 100644 include/Compiler/TypePrinter.h delete mode 100644 include/Support/ADT.h delete mode 100644 include/Support/Error.h rename include/{Server => Support}/Logger.h (96%) delete mode 100644 include/Support/Support.h rename src/{Compiler => AST}/Resolver.cpp (95%) rename src/{Compiler => AST}/Selection.cpp (81%) rename src/{Basic => AST}/SymbolKind.cpp (98%) rename src/{Compiler => AST}/Utility.cpp (94%) create mode 100644 src/Async/Network.cpp rename unittests/{Compiler => AST}/Resolver.cpp (99%) rename unittests/{Compiler => AST}/SemanticVisitor.cpp (78%) create mode 100644 unittests/Index/USR.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 440fce26..38841cd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(CLICE_PROJECT) set(CMAKE_CXX_STANDARD 23) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) if(NOT DEFINED LLVM_INSTALL_PATH OR LLVM_INSTALL_PATH STREQUAL "") message(FATAL_ERROR "Error: The variable LLVM_INSTALL_PATH is not set. Please specify it with -DLLVM_INSTALL_PATH=.") @@ -70,6 +70,7 @@ endif() # build clice core part as library file(GLOB_RECURSE CLICE_SOURCES + "${CMAKE_SOURCE_DIR}/src/AST/*.cpp" "${CMAKE_SOURCE_DIR}/src/Async/*.cpp" "${CMAKE_SOURCE_DIR}/src/Basic/*.cpp" "${CMAKE_SOURCE_DIR}/src/Compiler/*.cpp" diff --git a/include/Basic/RelationKind.h b/include/AST/RelationKind.h similarity index 100% rename from include/Basic/RelationKind.h rename to include/AST/RelationKind.h diff --git a/include/Compiler/Resolver.h b/include/AST/Resolver.h similarity index 64% rename from include/Compiler/Resolver.h rename to include/AST/Resolver.h index 02a50d11..5605b4c3 100644 --- a/include/Compiler/Resolver.h +++ b/include/AST/Resolver.h @@ -1,6 +1,13 @@ #pragma once -#include "Clang.h" +#include "clang/AST/Type.h" +#include "clang/AST/ExprCXX.h" + +namespace clang { + +class Sema; + +} namespace clice { @@ -16,9 +23,9 @@ public: clang::QualType resolve(clang::QualType type); - clang::ExprResult resolve(clang::CXXUnresolvedConstructExpr* expr); + void resolve(clang::CXXUnresolvedConstructExpr* expr); - clang::ExprResult resolve(clang::UnresolvedLookupExpr* expr); + void resolve(clang::UnresolvedLookupExpr* expr); // TODO: use a relative clear way to resolve `UnresolvedLookupExpr`. @@ -28,46 +35,48 @@ public: /// `decl` should be the declaration that the type is in. clang::QualType resugar(clang::QualType type, clang::Decl* decl); + using lookup_result = clang::DeclContext::lookup_result; + /// Look up the name in the given nested name specifier. - clang::lookup_result lookup(const clang::NestedNameSpecifier* NNS, clang::DeclarationName name); + lookup_result lookup(const clang::NestedNameSpecifier* NNS, clang::DeclarationName name); - clang::lookup_result lookup(const clang::DependentNameType* type) { + lookup_result lookup(const clang::DependentNameType* type) { return lookup(type->getQualifier(), type->getIdentifier()); } - clang::lookup_result lookup(const clang::DependentTemplateSpecializationType* type) { + lookup_result lookup(const clang::DependentTemplateSpecializationType* type) { return lookup(type->getQualifier(), type->getIdentifier()); } - clang::lookup_result lookup(const clang::DependentScopeDeclRefExpr* expr) { + lookup_result lookup(const clang::DependentScopeDeclRefExpr* expr) { return lookup(expr->getQualifier(), expr->getNameInfo().getName()); } - clang::lookup_result lookup(const clang::UnresolvedLookupExpr* expr) { - /// FIXME: + lookup_result lookup(const clang::UnresolvedLookupExpr* expr) { + /// FIXME: for(auto decl: expr->decls()) { if(auto TD = llvm::dyn_cast(decl)) { - return clang::lookup_result(TD); + return lookup_result(TD); } } return {}; } - clang::lookup_result lookup(const clang::UnresolvedMemberExpr* expr) { + lookup_result lookup(const clang::UnresolvedMemberExpr* expr) { return {}; } /// TODO: - clang::lookup_result lookup(clang::CXXDependentScopeMemberExpr* expr) { + lookup_result lookup(clang::CXXDependentScopeMemberExpr* expr) { return {}; } - clang::lookup_result lookup(const clang::UnresolvedUsingValueDecl* decl) { + lookup_result lookup(const clang::UnresolvedUsingValueDecl* decl) { return lookup(decl->getQualifier(), decl->getDeclName()); } - clang::lookup_result resolve(const clang::UnresolvedUsingTypenameDecl* decl) { + lookup_result resolve(const clang::UnresolvedUsingTypenameDecl* decl) { return lookup(decl->getQualifier(), decl->getDeclName()); } diff --git a/include/Compiler/Selection.h b/include/AST/Selection.h similarity index 88% rename from include/Compiler/Selection.h rename to include/AST/Selection.h index 1240f9c4..d278afcb 100644 --- a/include/Compiler/Selection.h +++ b/include/AST/Selection.h @@ -1,6 +1,8 @@ #pragma once -#include "Clang.h" +#include +#include "clang/AST/ASTTypeTraits.h" +#include "clang/Tooling/Syntax/Tokens.h" namespace clice { diff --git a/include/Compiler/Semantic.h b/include/AST/Semantic.h similarity index 97% rename from include/Compiler/Semantic.h rename to include/AST/Semantic.h index 9d071e79..4b7b1dcb 100644 --- a/include/Compiler/Semantic.h +++ b/include/AST/Semantic.h @@ -1,12 +1,11 @@ #pragma once -#include "Compilation.h" -#include "Resolver.h" #include "Utility.h" - -#include "Basic/RelationKind.h" -#include "Basic/SymbolKind.h" -#include "Support/Support.h" +#include "Resolver.h" +#include "SymbolKind.h" +#include "RelationKind.h" +#include "Compiler/Compilation.h" +#include "clang/AST/RecursiveASTVisitor.h" namespace clice { @@ -19,8 +18,6 @@ public: sema(info.sema()), pp(info.pp()), resolver(info.resolver()), srcMgr(info.srcMgr()), tokBuf(info.tokBuf()), info(info), mainFileOnly(mainFileOnly) {} -public: - public: consteval bool VisitImplicitInstantiation() { return true; @@ -34,13 +31,6 @@ public: return location.isInvalid() || (mainFileOnly && !srcMgr.isInMainFile(location)); } - void dump [[gnu::noinline]] (clang::SourceLocation loc) { - auto location = srcMgr.getPresumedLoc(loc); - llvm::SmallString<128> path; - auto err = fs::real_path(location.getFilename(), path); - llvm::outs() << path << ":" << location.getLine() << ":" << location.getColumn() << "\n"; - } - /// Invoked when a declaration occur is seen in source code. /// @param decl The decl corresponding to the symbol. /// @param kind The kind of the occurrence, such as declaration, definition, reference. @@ -122,7 +112,7 @@ public: } void run() { - Base::TraverseAST(sema.getASTContext()); + Base::TraverseAST(info.context()); for(auto directive: info.directives()) { for(auto macro: directive.second.macros) { @@ -141,7 +131,7 @@ public: } } - if(auto module = sema.getASTContext().getCurrentNamedModule()) { + if(auto module = info.context().getCurrentNamedModule()) { auto keyword = module->DefinitionLoc; auto begin = tokBuf.spelledTokenContaining(keyword); assert(begin->kind() == clang::tok::identifier && begin->text(srcMgr) == "module" && @@ -174,6 +164,14 @@ public: /// Declaration /// ============================================================================ +#define VISIT_DECL(type) bool Visit##type(const clang::type* decl) +#define VISIT_STMT(type) bool Visit##type(const clang::type* stmt) +#define VISIT_EXPR(type) bool Visit##type(const clang::type* expr) +#define VISIT_TYPE(type) bool Visit##type(const clang::type* type) +#define VISIT_TYPELOC(type) bool Visit##type(clang::type loc) + +#define TRAVERSE_DECL(type) bool Traverse##type(clang::type* decl) + TRAVERSE_DECL(Decl) { if(!decl) { return true; diff --git a/include/AST/SourceLocation.h b/include/AST/SourceLocation.h new file mode 100644 index 00000000..cdba4b64 --- /dev/null +++ b/include/AST/SourceLocation.h @@ -0,0 +1,35 @@ +#pragma once + +#include "clang/Basic/SourceLocation.h" + +namespace std { + +template <> +struct tuple_size : std::integral_constant {}; + +template <> +struct tuple_element<0, clang::SourceRange> { + using type = clang::SourceLocation; +}; + +template <> +struct tuple_element<1, clang::SourceRange> { + using type = clang::SourceLocation; +}; + +} // namespace std + +namespace clang { + +/// Through ADL, make `clang::SourceRange` could be destructured. +template +clang::SourceLocation get(clang::SourceRange range) { + if constexpr(I == 0) { + return range.getBegin(); + } else { + return range.getEnd(); + } +} + +} // namespace clang + diff --git a/include/Basic/SymbolKind.h b/include/AST/SymbolKind.h similarity index 100% rename from include/Basic/SymbolKind.h rename to include/AST/SymbolKind.h diff --git a/include/Compiler/Utility.h b/include/AST/Utility.h similarity index 96% rename from include/Compiler/Utility.h rename to include/AST/Utility.h index 22102189..5648b50a 100644 --- a/include/Compiler/Utility.h +++ b/include/AST/Utility.h @@ -1,4 +1,4 @@ -#include "Clang.h" +#include "clang/AST/Decl.h" namespace clice { diff --git a/include/Async/Async.h b/include/Async/Async.h index 1aba1f48..1554f5fb 100644 --- a/include/Async/Async.h +++ b/include/Async/Async.h @@ -2,4 +2,4 @@ #include "Scheduler.h" #include "FileSystem.h" -#include "Socket.h" \ No newline at end of file +#include "Network.h" diff --git a/include/Async/FileSystem.h b/include/Async/FileSystem.h index f5e2b10e..1f90ca45 100644 --- a/include/Async/FileSystem.h +++ b/include/Async/FileSystem.h @@ -13,17 +13,11 @@ namespace clice::async { -struct None {}; +template +using Result = std::expected; template -using Result = std::conditional_t, - std::expected, - std::expected>; - -template -using AsyncResult = std::conditional_t, - Task>, - Task>>; +using AsyncResult = Task>; namespace fs { diff --git a/include/Async/Socket.h b/include/Async/Network.h similarity index 72% rename from include/Async/Socket.h rename to include/Async/Network.h index 8b9a8083..fe46b9e0 100644 --- a/include/Async/Socket.h +++ b/include/Async/Network.h @@ -8,7 +8,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/FunctionExtras.h" -namespace clice::async { +namespace clice::async::net { using Callback = llvm::unique_function(json::Value)>; @@ -16,12 +16,13 @@ using Callback = llvm::unique_function(json::Value)>; void listen(Callback callback); /// Listen on the given ip and port, callback is called when there is a LSP message available. -void listen(Callback callback, const char* ip, unsigned int port); +void listen(const char* ip, unsigned int port, Callback callback); /// Spawn a new process and listen on its stdin/stdout. -void spawn(Callback callback, llvm::StringRef path, llvm::ArrayRef args); +void spawn(llvm::StringRef path, llvm::ArrayRef args, Callback callback); /// Write a JSON value to the client. Task<> write(json::Value value); -} // namespace clice::async +} // namespace clice::async::net + diff --git a/include/Async/libuv.h b/include/Async/libuv.h index eb4971ce..6af74307 100644 --- a/include/Async/libuv.h +++ b/include/Async/libuv.h @@ -15,18 +15,11 @@ #include #include -#include "Support/Error.h" - namespace clice::async { /// The default event loop. extern uv_loop_t* loop; -#define uv_check_call(func, ...) \ - if(int error = func(__VA_ARGS__); error < 0) { \ - log::fatal("An error occurred in " #func ": {0}", uv_strerror(error)); \ - } - template T& uv_cast(U* u) { assert(u && u->data && "uv_cast: invalid uv handle"); @@ -35,6 +28,4 @@ T& uv_cast(U* u) { const std::error_category& category(); - - } // namespace clice::async diff --git a/include/Basic/HeaderContext.h b/include/Basic/HeaderContext.h deleted file mode 100644 index 456f27d4..00000000 --- a/include/Basic/HeaderContext.h +++ /dev/null @@ -1,34 +0,0 @@ -#include "llvm/ADT/StringRef.h" - -namespace clice { - -/// Represents a source location in a file, it is corresponding -/// to the `clang::SourceLocation` but decoded. -struct SourceLocation { - /// The line number (1-based). - uint32_t line; - - /// The column number (1-based). - uint32_t column; - - /// The file name. - std::string filename; -}; - -/// Describes the context of a header file to uniquely identify its AST. -/// A header file may generate different ASTs depending on the inclusion context. -/// Even within the same source file, the AST may vary at different locations due -/// to preprocessor directives, macro definitions, or other compilation settings. -struct HeaderContext { - /// The compilation command used to generate the AST for the source file. - std::string command; - - /// The inclusion chain of the header file. - /// - The first element represents the header file itself. - /// - The last element represents the top-level header file included in - /// the source file that leads to this header. - /// This chain helps reconstruct the inclusion context for the header file. - std::vector includeChain; -}; - -} // namespace clice diff --git a/include/Basic/SourceCode.h b/include/Basic/SourceCode.h index 70a3fac2..de720d77 100644 --- a/include/Basic/SourceCode.h +++ b/include/Basic/SourceCode.h @@ -1,8 +1,8 @@ #pragma once -#include "llvm/ADT/FunctionExtras.h" +#include "AST/SourceLocation.h" #include "clang/Lex/Token.h" -#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/FunctionExtras.h" namespace clice { diff --git a/include/Compiler/AST.h b/include/Compiler/AST.h index e98e663f..3474d5c8 100644 --- a/include/Compiler/AST.h +++ b/include/Compiler/AST.h @@ -1,9 +1,10 @@ #pragma once -#include "Resolver.h" #include "Directive.h" - +#include "AST/Resolver.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Syntax/Tokens.h" namespace clice { diff --git a/include/Compiler/Clang.h b/include/Compiler/Clang.h deleted file mode 100644 index 8b8f49ae..00000000 --- a/include/Compiler/Clang.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Support/Support.h" - -#include "clang/Lex/Preprocessor.h" -#include "clang/AST/ASTTypeTraits.h" -#include "clang/AST/RecursiveASTVisitor.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Tooling/Syntax/Tokens.h" -#include "clang/Sema/Sema.h" - -namespace std { - -template <> -struct tuple_size : std::integral_constant {}; - -template <> -struct tuple_element<0, clang::SourceRange> { - using type = clang::SourceLocation; -}; - -template <> -struct tuple_element<1, clang::SourceRange> { - using type = clang::SourceLocation; -}; - -} // namespace std - -namespace clang { - -template -clang::SourceLocation get(clang::SourceRange range) { - if constexpr(I == 0) { - return range.getBegin(); - } else { - return range.getEnd(); - } -} - -#define VISIT_DECL(type) bool Visit##type(const clang::type* decl) -#define VISIT_STMT(type) bool Visit##type(const clang::type* stmt) -#define VISIT_EXPR(type) bool Visit##type(const clang::type* expr) -#define VISIT_TYPE(type) bool Visit##type(const clang::type* type) -#define VISIT_TYPELOC(type) bool Visit##type(clang::type loc) - -#define TRAVERSE_DECL(type) bool Traverse##type(clang::type* decl) - -using lookup_result = clang::DeclContext::lookup_result; - -} // namespace clang diff --git a/include/Compiler/Command.h b/include/Compiler/Command.h index 31e61506..3a27dafd 100644 --- a/include/Compiler/Command.h +++ b/include/Compiler/Command.h @@ -1,6 +1,8 @@ #pragma once -#include "Clang.h" +#include +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallVector.h" namespace clice { @@ -12,8 +14,8 @@ namespace clice { /// @param command The raw shell-escaped compile command. /// @param out A vector to hold pointers to the processed arguments. /// @param buffer A storage buffer for the actual argument strings. -llvm::Error mangleCommand(llvm::StringRef command, - llvm::SmallVectorImpl& out, - llvm::SmallVectorImpl& buffer); +std::expected mangleCommand(llvm::StringRef command, + llvm::SmallVectorImpl& out, + llvm::SmallVectorImpl& buffer); } // namespace clice diff --git a/include/Compiler/Compilation.h b/include/Compiler/Compilation.h index 086cb9d1..f84fd961 100644 --- a/include/Compiler/Compilation.h +++ b/include/Compiler/Compilation.h @@ -3,6 +3,7 @@ #include "AST.h" #include "Module.h" #include "Preamble.h" +#include "Support/FileSystem.h" namespace clice { @@ -55,15 +56,15 @@ std::unique_ptr createInstance(CompilationParams& param /// Build AST from given file path and content. If pch or pcm provided, apply them to the compiler. /// Note this function will not check whether we need to update the PCH or PCM, caller should check /// their reusability and update in time. -llvm::Expected compile(CompilationParams& params); +std::expected compile(CompilationParams& params); /// Run code completion at the given location. -llvm::Expected compile(CompilationParams& params, clang::CodeCompleteConsumer* consumer); +std::expected compile(CompilationParams& params, clang::CodeCompleteConsumer* consumer); /// Build PCH from given file path and content. -llvm::Expected compile(CompilationParams& params, PCHInfo& out); +std::expected compile(CompilationParams& params, PCHInfo& out); /// Build PCM from given file path and content. -llvm::Expected compile(CompilationParams& params, PCMInfo& out); +std::expected compile(CompilationParams& params, PCMInfo& out); } // namespace clice diff --git a/include/Compiler/Diagnostic.h b/include/Compiler/Diagnostic.h index 24a4a23e..c8ac07fe 100644 --- a/include/Compiler/Diagnostic.h +++ b/include/Compiler/Diagnostic.h @@ -1,6 +1,6 @@ #pragma once -#include "Clang.h" +#include "clang/Basic/Diagnostic.h" namespace clice { diff --git a/include/Compiler/Directive.h b/include/Compiler/Directive.h index d71b0622..68a72443 100644 --- a/include/Compiler/Directive.h +++ b/include/Compiler/Directive.h @@ -1,6 +1,8 @@ #pragma once -#include "Clang.h" +#include "AST/SourceLocation.h" +#include "clang/Lex/MacroInfo.h" +#include "llvm/ADT/DenseMap.h" namespace clice { diff --git a/include/Compiler/Module.h b/include/Compiler/Module.h index c182c09b..23c080ae 100644 --- a/include/Compiler/Module.h +++ b/include/Compiler/Module.h @@ -2,9 +2,9 @@ #include #include +#include #include "Support/Struct.h" -#include "Support/Error.h" namespace clice { @@ -41,8 +41,6 @@ std::string scanModuleName(CompilationParams& params); /// Run the preprocessor to scan the given module unit to /// collect its module name and dependencies. -llvm::Expected scanModule(CompilationParams& params); - - +std::expected scanModule(CompilationParams& params); } // namespace clice diff --git a/include/Compiler/Preamble.h b/include/Compiler/Preamble.h index d234c20d..8069a6d5 100644 --- a/include/Compiler/Preamble.h +++ b/include/Compiler/Preamble.h @@ -2,8 +2,9 @@ #include #include +#include -#include "Support/Error.h" +#include "llvm/ADT/StringRef.h" namespace clice { diff --git a/include/Compiler/TypePrinter.h b/include/Compiler/TypePrinter.h deleted file mode 100644 index e69de29b..00000000 diff --git a/include/Feature/SemanticTokens.h b/include/Feature/SemanticTokens.h index 59851487..1a3505a8 100644 --- a/include/Feature/SemanticTokens.h +++ b/include/Feature/SemanticTokens.h @@ -1,7 +1,7 @@ #pragma once +#include "AST/SymbolKind.h" #include "Basic/Document.h" -#include "Basic/SymbolKind.h" #include "Basic/SourceCode.h" #include "Basic/SourceConverter.h" #include "Index/Shared.h" diff --git a/include/Index/SymbolIndex.h b/include/Index/SymbolIndex.h index 57350b3b..9a47f7d1 100644 --- a/include/Index/SymbolIndex.h +++ b/include/Index/SymbolIndex.h @@ -2,9 +2,9 @@ #include "Shared.h" #include "ArrayView.h" +#include "AST/SymbolKind.h" +#include "AST/RelationKind.h" #include "Basic/SourceCode.h" -#include "Basic/SymbolKind.h" -#include "Basic/RelationKind.h" #include "Support/JSON.h" namespace clice { diff --git a/include/Index/USR.h b/include/Index/USR.h index e69de29b..fa575a70 100644 --- a/include/Index/USR.h +++ b/include/Index/USR.h @@ -0,0 +1,15 @@ +#pragma once + +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" + +namespace clice::index { + +bool generateUSRForDecl(const clang::Decl* D, llvm::SmallVectorImpl& buffer); + +bool generateUSRForMacro(llvm::StringRef name, + clang::SourceLocation location, + const clang::SourceManager& SM, + llvm::SmallVectorImpl& buffer); + +} // namespace clice::index diff --git a/include/Server/Config.h b/include/Server/Config.h index 1cff2a1f..b563545d 100644 --- a/include/Server/Config.h +++ b/include/Server/Config.h @@ -1,6 +1,9 @@ #pragma once -#include "Support/Support.h" +#include + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" namespace clice::config { diff --git a/include/Server/Indexer.h b/include/Server/Indexer.h index a99d4332..d63bd96d 100644 --- a/include/Server/Indexer.h +++ b/include/Server/Indexer.h @@ -1,10 +1,10 @@ #pragma once -#include "Async/Async.h" #include "Config.h" #include "Database.h" #include "Protocol.h" -#include "Basic/RelationKind.h" +#include "Async/Async.h" +#include "AST/RelationKind.h" #include "llvm/ADT/DenseSet.h" diff --git a/include/Support/ADT.h b/include/Support/ADT.h deleted file mode 100644 index f65f0436..00000000 --- a/include/Support/ADT.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringExtras.h" - -namespace clice { - -namespace ranges = std::ranges; -namespace views = std::ranges::views; - -} // namespace clice diff --git a/include/Support/Compare.h b/include/Support/Compare.h index e45713d7..849a6419 100644 --- a/include/Support/Compare.h +++ b/include/Support/Compare.h @@ -1,6 +1,7 @@ #pragma once -#include "ADT.h" +#include + #include "Enum.h" #include "Struct.h" diff --git a/include/Support/Enum.h b/include/Support/Enum.h index 80ea2b43..cdf148dd 100644 --- a/include/Support/Enum.h +++ b/include/Support/Enum.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/include/Support/Error.h b/include/Support/Error.h deleted file mode 100644 index a7054316..00000000 --- a/include/Support/Error.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "llvm/Support/Error.h" -#include "llvm/Support/FormatVariadic.h" - -#include -#include - -namespace clice { - -template -llvm::Error error(const char* fmt, Args&&... args) { - return llvm::make_error( - llvm::formatv(fmt, std::forward(args)...).str(), - llvm::inconvertibleErrorCode()); -} - -} // namespace clice diff --git a/include/Support/Format.h b/include/Support/Format.h index ac42e002..03c2b0b2 100644 --- a/include/Support/Format.h +++ b/include/Support/Format.h @@ -2,9 +2,9 @@ #include -#include "Support/Error.h" #include "Support/JSON.h" #include "Support/Ranges.h" +#include "llvm/Support/Error.h" namespace clice { diff --git a/include/Server/Logger.h b/include/Support/Logger.h similarity index 96% rename from include/Server/Logger.h rename to include/Support/Logger.h index c0158de8..6161b61c 100644 --- a/include/Server/Logger.h +++ b/include/Support/Logger.h @@ -1,7 +1,7 @@ #pragma once -#include "Support/Format.h" -#include "Support/FileSystem.h" +#include "Format.h" +#include "FileSystem.h" namespace clice::log { diff --git a/include/Support/Support.h b/include/Support/Support.h deleted file mode 100644 index 43b30dc0..00000000 --- a/include/Support/Support.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "ADT.h" -#include "Enum.h" -#include "Struct.h" -#include "Hash.h" -#include "Format.h" -#include "JSON.h" -#include "Compare.h" -#include "FileSystem.h" - -#include "llvm/Support/MemoryBuffer.h" diff --git a/include/Test/CTest.h b/include/Test/CTest.h index f3c71992..c59811c4 100644 --- a/include/Test/CTest.h +++ b/include/Test/CTest.h @@ -1,10 +1,7 @@ #pragma once #include "Test.h" -#include "Basic/Location.h" #include "Compiler/Compilation.h" -#include "Support/Support.h" -#include namespace clice::testing { diff --git a/include/Test/Test.h b/include/Test/Test.h index 63401664..ab7b4d7f 100644 --- a/include/Test/Test.h +++ b/include/Test/Test.h @@ -2,8 +2,12 @@ #include "gtest/gtest.h" #include "Basic/Location.h" +#include "Support/JSON.h" +#include "Support/Format.h" +#include "Support/Compare.h" +#include "Support/FileSystem.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringMap.h" -#include "Support/Support.h" namespace clice::testing { diff --git a/src/Compiler/Resolver.cpp b/src/AST/Resolver.cpp similarity index 95% rename from src/Compiler/Resolver.cpp rename to src/AST/Resolver.cpp index 734be2dc..08ef473e 100644 --- a/src/Compiler/Resolver.cpp +++ b/src/AST/Resolver.cpp @@ -1,9 +1,8 @@ -#include "Support/Support.h" -#include -#include -#include -#include -#include +#include "AST/Resolver.h" +#include "Support/Format.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/TreeTransform.h" +#include "clang/Sema/TemplateDeduction.h" namespace clice { @@ -295,22 +294,24 @@ public: return true; } + using lookup_result = clang::DeclContext::lookup_result; + /// If this class and its base class have members with the same name, `DeclContext::lookup` /// will return multiple declarations in order from the base class to the derived class, so we /// use the last declaration. - clang::Decl* preferred(clang::lookup_result members) { + clang::Decl* preferred(lookup_result members) { clang::Decl* decl = nullptr; std::ranges::for_each(members, [&](auto member) { decl = member; }); return decl; } - clang::lookup_result lookup(clang::QualType type, clang::DeclarationName name) { + lookup_result lookup(clang::QualType type, clang::DeclarationName name) { clang::Decl* TD = nullptr; llvm::ArrayRef args; type = TransformType(type); if(type.isNull()) { - return clang::lookup_result(); + return lookup_result(); } if(auto TST = type->getAs()) { @@ -324,7 +325,7 @@ public: } if(!TD) { - return clang::lookup_result(); + return lookup_result(); } #ifndef NDEBUG @@ -342,14 +343,13 @@ public: } } - return clang::lookup_result(); + return lookup_result(); } /// Look up the name in the given nested name specifier. - clang::lookup_result lookup(const clang::NestedNameSpecifier* NNS, - clang::DeclarationName name) { + lookup_result lookup(const clang::NestedNameSpecifier* NNS, clang::DeclarationName name) { if(!NNS) { - return clang::lookup_result(); + return lookup_result(); } /// Search the resolved entities first. @@ -388,9 +388,9 @@ public: } /// Look up the name in the bases of the given class. Keep stack unchanged. - clang::lookup_result lookupInBases(clang::CXXRecordDecl* CRD, clang::DeclarationName name) { + lookup_result lookupInBases(clang::CXXRecordDecl* CRD, clang::DeclarationName name) { if(!CRD->hasDefinition()) { - return clang::lookup_result(); + return lookup_result(); } for(auto base: CRD->bases()) { @@ -403,19 +403,19 @@ public: } } - return clang::lookup_result(); + return lookup_result(); } /// Look up the name in the given class template. We first search the name in the /// primary template, if failed, try dependent base classes, if still failed, try /// partial specializations. **Note that this function will be responsible for pushing /// the class template and its template arguments to the instantiation stack**. - clang::lookup_result lookup(clang::ClassTemplateDecl* CTD, - clang::DeclarationName name, - TemplateArguments visibleArguments) { + lookup_result lookup(clang::ClassTemplateDecl* CTD, + clang::DeclarationName name, + TemplateArguments visibleArguments) { llvm::SmallVector arguments; if(!checkTemplateArguments(CTD, visibleArguments, arguments)) { - return clang::lookup_result(); + return lookup_result(); } /// Try to find the name in the partial specializations. @@ -454,7 +454,7 @@ public: /// FIXME: try full specializations?. - return clang::lookup_result(); + return lookup_result(); } /// Instantiate the given type and clear the instantiation stack. @@ -706,10 +706,10 @@ public: } auto NNS = TransformNestedNameSpecifierLoc(TL.getQualifierLoc()).getNestedNameSpecifier(); - if(!NNS){ + if(!NNS) { return clang::QualType(); } - + /// FIXME: figure out here. clang::TemplateArgumentListInfo info; using iterator = clang::TemplateArgumentLocContainerIterator< @@ -787,8 +787,8 @@ clang::QualType TemplateResolver::resugar(clang::QualType type, clang::Decl* dec return resugar.TransformType(type); } -clang::lookup_result TemplateResolver::lookup(const clang::NestedNameSpecifier* NNS, - clang::DeclarationName name) { +TemplateResolver::lookup_result TemplateResolver::lookup(const clang::NestedNameSpecifier* NNS, + clang::DeclarationName name) { PseudoInstantiator instantiator(sema, resolved); return instantiator.lookup(NNS, name); } diff --git a/src/Compiler/Selection.cpp b/src/AST/Selection.cpp similarity index 81% rename from src/Compiler/Selection.cpp rename to src/AST/Selection.cpp index 93baf213..dee8cc2f 100644 --- a/src/Compiler/Selection.cpp +++ b/src/AST/Selection.cpp @@ -1,8 +1,7 @@ - - #include -#include +#include "AST/Selection.h" +#include "clang/AST/RecursiveASTVisitor.h" namespace clice { @@ -19,19 +18,23 @@ public: auto& sm = context.getSourceManager(); // FIXME: support other file. auto tokens = buffer.spelledTokens(sm.getMainFileID()); - left = std::to_address(std::partition_point(tokens.begin(), tokens.end(), [&](const auto& token) { - // int xxxx = 3; - // ^^^^^^ - // expect to find the first token whose end location is greater than or equal to `begin`. - return sm.getFileOffset(token.endLocation()) < begin; - })); + left = std::to_address( + std::partition_point(tokens.begin(), tokens.end(), [&](const auto& token) { + // int xxxx = 3; + // ^^^^^^ + // expect to find the first token whose end location is greater than or equal to + // `begin`. + return sm.getFileOffset(token.endLocation()) < begin; + })); - rigth = std::to_address(std::partition_point(tokens.rbegin(), tokens.rend(), [&](const auto& token) { - // int xxxx = 3; - // ^^^^^^ - // expect to find the first token whose start location is less than or equal to `end`. - return sm.getFileOffset(token.location()) > end; - })); + rigth = std::to_address( + std::partition_point(tokens.rbegin(), tokens.rend(), [&](const auto& token) { + // int xxxx = 3; + // ^^^^^^ + // expect to find the first token whose start location is less than or equal to + // `end`. + return sm.getFileOffset(token.location()) > end; + })); if(left == tokens.end() || rigth == tokens.end()) { std::terminate(); @@ -156,21 +159,15 @@ public: return Base::TraverseDecl(decl); } - return builder.hook(decl, [&] { - return Base::TraverseDecl(decl); - }); + return builder.hook(decl, [&] { return Base::TraverseDecl(decl); }); } bool TraverseStmt(clang::Stmt* stmt) { - return builder.hook(stmt, [&] { - return Base::TraverseStmt(stmt); - }); + return builder.hook(stmt, [&] { return Base::TraverseStmt(stmt); }); } bool TraverseAttr(clang::Attr* attr) { - return builder.hook(attr, [&] { - return Base::TraverseAttr(attr); - }); + return builder.hook(attr, [&] { return Base::TraverseAttr(attr); }); } /// we don't care about the node without location information, so skip them. @@ -193,33 +190,23 @@ public: return TraverseTypeLoc(QTL.getUnqualifiedLoc()); } - return builder.hook(&loc, [&] { - return Base::TraverseTypeLoc(loc); - }); + return builder.hook(&loc, [&] { return Base::TraverseTypeLoc(loc); }); } bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc NNS) { - return builder.hook(&NNS, [&] { - return Base::TraverseNestedNameSpecifierLoc(NNS); - }); + return builder.hook(&NNS, [&] { return Base::TraverseNestedNameSpecifierLoc(NNS); }); } bool TraverseTemplateArgumentLoc(const clang::TemplateArgumentLoc& argument) { - return builder.hook(&argument, [&] { - return Base::TraverseTemplateArgumentLoc(argument); - }); + return builder.hook(&argument, [&] { return Base::TraverseTemplateArgumentLoc(argument); }); } bool TraverseCXXBaseSpecifier(const clang::CXXBaseSpecifier& base) { - return builder.hook(&base, [&] { - return Base::TraverseCXXBaseSpecifier(base); - }); + return builder.hook(&base, [&] { return Base::TraverseCXXBaseSpecifier(base); }); } bool TraverseConstructorInitializer(clang::CXXCtorInitializer* init) { - return builder.hook(init, [&] { - return Base::TraverseConstructorInitializer(init); - }); + return builder.hook(init, [&] { return Base::TraverseConstructorInitializer(init); }); } // bool TraverseDeclarationNameInfo(clang::DeclarationNameInfo info) { diff --git a/src/Basic/SymbolKind.cpp b/src/AST/SymbolKind.cpp similarity index 98% rename from src/Basic/SymbolKind.cpp rename to src/AST/SymbolKind.cpp index a4d6a996..cdce32da 100644 --- a/src/Basic/SymbolKind.cpp +++ b/src/AST/SymbolKind.cpp @@ -1,4 +1,4 @@ -#include "Basic/SymbolKind.h" +#include "AST/SymbolKind.h" #include "Compiler/Compilation.h" namespace clice { diff --git a/src/Compiler/Utility.cpp b/src/AST/Utility.cpp similarity index 94% rename from src/Compiler/Utility.cpp rename to src/AST/Utility.cpp index f488f4ec..d4724ef9 100644 --- a/src/Compiler/Utility.cpp +++ b/src/AST/Utility.cpp @@ -1,4 +1,8 @@ -#include +#include "AST/Utility.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceManager.h" namespace clice { @@ -42,8 +46,8 @@ const clang::NamedDecl* instantiatedFrom(const clang::NamedDecl* decl) { auto kind = CTSD->getTemplateSpecializationKind(); if(kind == clang::TSK_Undeclared) { - /// The instantiation of template is lazy, in this case, the specialization is undeclared. - /// Temporarily return primary template of the specialization. + /// The instantiation of template is lazy, in this case, the specialization is + /// undeclared. Temporarily return primary template of the specialization. /// FIXME: Is there a better way to handle such case? return CTSD->getSpecializedTemplate()->getTemplatedDecl(); } else if(kind == clang::TSK_ExplicitSpecialization) { @@ -167,9 +171,7 @@ const clang::NamedDecl* declForType(clang::QualType type) { return decl->getTemplatedDecl(); } - if(llvm::isa(decl)) { + if(llvm::isa(decl)) { return decl; } diff --git a/src/Async/Async.cpp b/src/Async/Async.cpp index 7ca8246e..d8b7ba9c 100644 --- a/src/Async/Async.cpp +++ b/src/Async/Async.cpp @@ -1,7 +1,7 @@ #include #include "Async/Async.h" -#include "Server/Logger.h" +#include "Support/Logger.h" namespace clice::async { @@ -13,7 +13,7 @@ namespace { /// The task queue waiting for resuming. std::deque> tasks; -Callback callback = {}; +net::Callback callback = {}; uv_stream_t* writer = {}; @@ -22,21 +22,6 @@ bool listened = false; } // namespace -/// This function is called by the event loop to resume the tasks. -static void event_loop(uv_idle_t* handle) { - if(tasks.empty()) { - return; - } - - auto task = tasks.front(); - tasks.pop_front(); - task.resume(); - - if(tasks.empty() && !listened) { - uv_stop(loop); - } -} - void schedule(std::coroutine_handle<> core) { uv_async_t* async = new uv_async_t; async->data = core.address(); @@ -49,9 +34,6 @@ void schedule(std::coroutine_handle<> core) { } void run() { - // uv_idle_t idle; - // uv_idle_init(loop, &idle); - // uv_idle_start(&idle, event_loop); #ifdef _WIN32 _putenv_s("UV_THREADPOOL_SIZE", "20"); #else @@ -60,262 +42,4 @@ void run() { uv_run(loop, UV_RUN_DEFAULT); } -namespace { - -void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - /// This function is called synchronously before `on_read`. See the implementation of - /// `uv__read` in libuv/src/unix/stream.c. So it is safe to use a static buffer here. - static llvm::SmallString<65536> buffer; - buffer.resize_for_overwrite(suggested_size); - buf->base = buffer.data(); - buf->len = suggested_size; -} - -class MessageBuffer { -public: - MessageBuffer() = default; - - void append(llvm::StringRef message) { - buffer += message; - } - - llvm::StringRef peek() { - llvm::StringRef str = buffer; - std::size_t length = 0; - if(str.consume_front("Content-Length: ") && !str.consumeInteger(10, length) && - str.consume_front("\r\n\r\n") && str.size() >= length) { - auto result = str.substr(0, length); - pos = result.end() - buffer.begin(); - return result; - } - return {}; - } - - void consume() { - buffer.erase(buffer.begin(), buffer.begin() + pos); - pos = 0; - } - -private: - std::size_t pos; - llvm::SmallString<4096> buffer; -}; - -void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - /// We have at most one connection and use default event loop. So there is no data race - /// risk. It is safe to use a static buffer here. - - /// FIXME: use a more efficient data structure. - static MessageBuffer buffer; - if(nread > 0) { - buffer.append({buf->base, static_cast(nread)}); - if(auto message = buffer.peek(); !message.empty()) { - if(auto json = json::parse(message)) { - /// This is a top-level coroutine. - auto core = callback(std::move(*json)); - /// It will be destroyed in final suspend point. - /// So we release it here. - async::schedule(core.release()); - buffer.consume(); - } else { - log::fatal("An error occurred while parsing JSON: {0}", json.takeError()); - } - } - } else if(nread < 0) { - if(nread != UV_EOF) { - log::fatal("An error occurred while reading: {0}", uv_strerror(nread)); - } - uv_close((uv_handle_t*)stream, NULL); - } -} - -} // namespace - -void listen(Callback callback) { - static uv_pipe_t in; - static uv_pipe_t out; - - async::callback = std::move(callback); - writer = reinterpret_cast(&out); - - uv_check_call(uv_pipe_init, async::loop, &in, 0); - uv_check_call(uv_pipe_init, async::loop, &out, 0); - - uv_check_call(uv_pipe_open, &in, 0); - uv_check_call(uv_pipe_open, &out, 1); - - uv_check_call(uv_read_start, (uv_stream_t*)&in, async::on_alloc, async::on_read); - - log::info("Server started in pipe mode"); - async::listened = true; -} - -void listen(Callback callback, const char* ip, unsigned int port) { - static uv_tcp_t server; - static uv_tcp_t client; - - async::callback = std::move(callback); - writer = reinterpret_cast(&client); - - uv_check_call(uv_tcp_init, async::loop, &server); - uv_check_call(uv_tcp_init, async::loop, &client); - - struct ::sockaddr_in addr; - uv_check_call(uv_ip4_addr, ip, port, &addr); - uv_check_call(uv_tcp_bind, &server, (const struct sockaddr*)&addr, 0); - - auto on_connection = [](uv_stream_t* server, int status) { - if(status < 0) { - log::fatal("An error occurred while listening: {0}", uv_strerror(status)); - } - - uv_check_call(uv_accept, server, (uv_stream_t*)&client); - log::info("New connection accepted"); - uv_check_call(uv_read_start, (uv_stream_t*)&client, async::on_alloc, async::on_read); - }; - - uv_check_call(uv_listen, (uv_stream_t*)&server, 128, on_connection); - - log::info("Server started in socket mode at {0}:{1}", ip, port); - async::listened = true; -} - -void spawn(Callback callback, llvm::StringRef path, llvm::ArrayRef args) { - static uv_pipe_t in; - static uv_pipe_t out; - static uv_pipe_t err; - - async::callback = std::move(callback); - writer = reinterpret_cast(&in); - - uv_check_call(uv_pipe_init, async::loop, &in, 0); - uv_check_call(uv_pipe_init, async::loop, &out, 0); - uv_check_call(uv_pipe_init, async::loop, &err, 0); - - static uv_process_t process; - static uv_process_options_t options; - - static uv_stdio_container_t stdio[3]; - stdio[0].flags = static_cast(UV_CREATE_PIPE | UV_READABLE_PIPE); - stdio[0].data.stream = (uv_stream_t*)∈ - - stdio[1].flags = static_cast(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - stdio[1].data.stream = (uv_stream_t*)&out; - - stdio[2].flags = static_cast(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - stdio[2].data.stream = (uv_stream_t*)&err; - - options = {[](uv_process_t* req, int64_t exit_status, int term_signal) { - printf("Child process exited with status %ld, signal %d\n", exit_status, term_signal); - uv_close((uv_handle_t*)req, NULL); - }}; - options.stdio = stdio; - options.stdio_count = 3; - - static llvm::SmallString<128> file = path; - options.file = file.c_str(); - - static llvm::SmallString<1024> buffer; - static llvm::SmallVector argv; - std::size_t size = 0; - size += path.size() + 1; - for(auto& arg: args) { - size += arg.size() + 1; - } - buffer.resize_for_overwrite(size); - argv.push_back(buffer.end()); - buffer.append(path); - buffer.push_back('\0'); - for(auto& arg: args) { - argv.push_back(buffer.end()); - buffer.append(arg); - buffer.push_back('\0'); - } - options.args = argv.data(); - - uv_check_call(uv_spawn, async::loop, &process, &options); - uv_check_call(uv_read_start, (uv_stream_t*)&out, async::on_alloc, async::on_read); - uv_check_call( - uv_read_start, - (uv_stream_t*)&err, - async::on_alloc, - [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - if(nread > 0) { - log::warn("{0}", llvm::StringRef{buf->base, static_cast(nread)}); - } else if(nread < 0) { - if(nread != UV_EOF) { - log::fatal("An error occurred while reading: {0}", uv_strerror(nread)); - } - uv_close((uv_handle_t*)stream, NULL); - } - }); - - log::info("Process spawned: {0}", path); - async::listened = true; - - auto on_signal = +[](uv_signal_t* handle, int signum) { - fprintf(stderr, "Signal received: %d, killing child process\n", signum); - uv_process_kill(&process, SIGKILL); - uv_signal_stop(handle); - uv_close((uv_handle_t*)handle, NULL); - uv_stop(loop); - }; - - static uv_signal_t signal; - uv_signal_init(loop, &signal); - uv_signal_start(&signal, on_signal, SIGINT); - - static uv_signal_t signal2; - uv_signal_init(loop, &signal2); - uv_signal_start(&signal2, on_signal, SIGTERM); - - static uv_signal_t signal3; - uv_signal_init(loop, &signal3); - uv_signal_start(&signal3, on_signal, SIGQUIT); - - static uv_signal_t signal4; - uv_signal_init(loop, &signal4); - uv_signal_start(&signal4, on_signal, SIGKILL); -} - -/// Write a JSON value to the client. -Task<> write(json::Value value) { - struct awaiter { - uv_write_t write; - uv_buf_t buf[2]; - llvm::SmallString<128> header; - llvm::SmallString<4096> message; - core_handle waiting; - - bool await_ready() const noexcept { - return false; - } - - void await_suspend(core_handle waiting) noexcept { - write.data = this; - - this->waiting = waiting; - buf[0] = uv_buf_init(header.data(), header.size()); - buf[1] = uv_buf_init(message.data(), message.size()); - - uv_check_call(uv_write, &write, writer, buf, 2, [](uv_write_t* req, int status) { - if(status < 0) { - log::fatal("An error occurred while writing: {0}", uv_strerror(status)); - } - - auto& awaiter = uv_cast(req); - async::schedule(awaiter.waiting); - }); - } - - void await_resume() noexcept {} - } awaiter; - - llvm::raw_svector_ostream(awaiter.message) << value; - llvm::raw_svector_ostream(awaiter.header) - << "Content-Length: " << awaiter.message.size() << "\r\n\r\n"; - - co_await awaiter; -} - } // namespace clice::async diff --git a/src/Async/FileSystem.cpp b/src/Async/FileSystem.cpp index 0b95c7f6..d7457133 100644 --- a/src/Async/FileSystem.cpp +++ b/src/Async/FileSystem.cpp @@ -53,7 +53,7 @@ struct fs { if constexpr(!std::is_void_v) { return static_cast(this)->result(); } else { - return None(); + return Result(); } } }; @@ -226,7 +226,7 @@ AsyncResult write(std::string path, char* buffer, std::size_t size, Mode m co_return std::unexpected(result.error()); } - co_return None(); + co_return Result(); } AsyncResult stat(std::string path) { diff --git a/src/Async/Network.cpp b/src/Async/Network.cpp new file mode 100644 index 00000000..01907e5e --- /dev/null +++ b/src/Async/Network.cpp @@ -0,0 +1,240 @@ +#include "Async/Network.h" +#include "Support/Logger.h" + +namespace clice::async::net { + +namespace { + +void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { + /// This function is called synchronously before `on_read`. See the implementation of + /// `uv__read` in libuv/src/unix/stream.c. So it is safe to use a static buffer here. + static llvm::SmallString<65536> buffer; + buffer.resize_for_overwrite(suggested_size); + buf->base = buffer.data(); + buf->len = suggested_size; +} + +class MessageBuffer { +public: + MessageBuffer() = default; + + void append(llvm::StringRef message) { + buffer += message; + } + + llvm::StringRef peek() { + llvm::StringRef str = buffer; + std::size_t length = 0; + if(str.consume_front("Content-Length: ") && !str.consumeInteger(10, length) && + str.consume_front("\r\n\r\n") && str.size() >= length) { + auto result = str.substr(0, length); + pos = result.end() - buffer.begin(); + return result; + } + return {}; + } + + void consume() { + buffer.erase(buffer.begin(), buffer.begin() + pos); + pos = 0; + } + +private: + std::size_t pos; + llvm::SmallString<4096> buffer; +}; + +net::Callback callback = {}; + +uv_stream_t* writer = {}; + +void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + /// We have at most one connection and use default event loop. So there is no data race + /// risk. It is safe to use a static buffer here. + + /// FIXME: use a more efficient data structure. + static MessageBuffer buffer; + if(nread > 0) { + buffer.append({buf->base, static_cast(nread)}); + if(auto message = buffer.peek(); !message.empty()) { + if(auto json = json::parse(message)) { + /// This is a top-level coroutine. + auto core = callback(std::move(*json)); + /// It will be destroyed in final suspend point. + /// So we release it here. + async::schedule(core.release()); + buffer.consume(); + } else { + log::fatal("An error occurred while parsing JSON: {0}", json.takeError()); + } + } + } else if(nread < 0) { + if(nread != UV_EOF) { + log::fatal("An error occurred while reading: {0}", uv_strerror(nread)); + } + uv_close((uv_handle_t*)stream, NULL); + } +} + +} // namespace + +#define uv_check_call(func, ...) \ + if(int error = func(__VA_ARGS__); error < 0) { \ + log::fatal("An error occurred while calling {0}: {1}", #func, uv_strerror(error)); \ + } + +#define uv_log(error) \ + if(error < 0) { \ + log::fatal("{}", std::error_code(error, std::system_category())); \ + } + +void listen(Callback callback) { + static uv_pipe_t in; + static uv_pipe_t out; + + net::callback = std::move(callback); + writer = reinterpret_cast(&out); + + uv_log(uv_pipe_init(async::loop, &in, 0)); + uv_log(uv_pipe_open(&in, 0)); + + uv_log(uv_pipe_init(async::loop, &out, 0)); + uv_log(uv_pipe_open(&out, 1)); + + uv_log(uv_read_start((uv_stream_t*)&in, net::on_alloc, net::on_read)); +} + +void listen(const char* ip, unsigned int port, Callback callback) { + static uv_tcp_t server; + static uv_tcp_t client; + + net::callback = std::move(callback); + writer = reinterpret_cast(&client); + + uv_log(uv_tcp_init(async::loop, &server)); + uv_log(uv_tcp_init(async::loop, &client)); + + struct ::sockaddr_in addr; + uv_log(uv_ip4_addr(ip, port, &addr)); + uv_log(uv_tcp_bind(&server, (const struct ::sockaddr*)&addr, 0)); + + auto on_connection = [](uv_stream_t* server, int status) { + uv_log(status); + uv_log(uv_accept(server, (uv_stream_t*)&client)); + uv_log(uv_read_start((uv_stream_t*)&client, net::on_alloc, net::on_read)); + }; + + uv_log(uv_listen((uv_stream_t*)&server, 1, on_connection)); +} + +void spawn(llvm::StringRef path, llvm::ArrayRef args, Callback callback) { + static uv_pipe_t in; + static uv_pipe_t out; + static uv_pipe_t err; + + net::callback = std::move(callback); + writer = reinterpret_cast(&in); + + uv_check_call(uv_pipe_init, async::loop, &in, 0); + uv_check_call(uv_pipe_init, async::loop, &out, 0); + uv_check_call(uv_pipe_init, async::loop, &err, 0); + + static uv_process_t process; + static uv_process_options_t options; + + static uv_stdio_container_t stdio[3]; + stdio[0].flags = static_cast(UV_CREATE_PIPE | UV_READABLE_PIPE); + stdio[0].data.stream = (uv_stream_t*)∈ + + stdio[1].flags = static_cast(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + stdio[1].data.stream = (uv_stream_t*)&out; + + stdio[2].flags = static_cast(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + stdio[2].data.stream = (uv_stream_t*)&err; + + options = {[](uv_process_t* req, int64_t exit_status, int term_signal) { + printf("Child process exited with status %ld, signal %d\n", exit_status, term_signal); + uv_close((uv_handle_t*)req, NULL); + }}; + options.stdio = stdio; + options.stdio_count = 3; + + static llvm::SmallString<128> file = path; + options.file = file.c_str(); + + static llvm::SmallString<1024> buffer; + static llvm::SmallVector argv; + std::size_t size = 0; + size += path.size() + 1; + for(auto& arg: args) { + size += arg.size() + 1; + } + buffer.resize_for_overwrite(size); + argv.push_back(buffer.end()); + buffer.append(path); + buffer.push_back('\0'); + for(auto& arg: args) { + argv.push_back(buffer.end()); + buffer.append(arg); + buffer.push_back('\0'); + } + options.args = argv.data(); + + uv_log(uv_spawn(async::loop, &process, &options)); + uv_log(uv_read_start((uv_stream_t*)&out, net::on_alloc, net::on_read)); + + auto on_read = [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + if(nread > 0) { + log::warn("{0}", llvm::StringRef{buf->base, static_cast(nread)}); + } else if(nread < 0) { + if(nread != UV_EOF) { + log::fatal("An error occurred while reading: {0}", uv_strerror(nread)); + } + uv_close((uv_handle_t*)stream, NULL); + } + }; + + uv_log(uv_read_start((uv_stream_t*)&err, net::on_alloc, on_read)); +} + +/// Write a JSON value to the client. +Task<> write(json::Value value) { + struct awaiter { + uv_write_t write; + uv_buf_t buf[2]; + llvm::SmallString<128> header; + llvm::SmallString<4096> message; + core_handle waiting; + + bool await_ready() const noexcept { + return false; + } + + void await_suspend(core_handle waiting) noexcept { + write.data = this; + + this->waiting = waiting; + buf[0] = uv_buf_init(header.data(), header.size()); + buf[1] = uv_buf_init(message.data(), message.size()); + + uv_check_call(uv_write, &write, writer, buf, 2, [](uv_write_t* req, int status) { + if(status < 0) { + log::fatal("An error occurred while writing: {0}", uv_strerror(status)); + } + + auto& awaiter = uv_cast(req); + async::schedule(awaiter.waiting); + }); + } + + void await_resume() noexcept {} + } awaiter; + + llvm::raw_svector_ostream(awaiter.message) << value; + llvm::raw_svector_ostream(awaiter.header) + << "Content-Length: " << awaiter.message.size() << "\r\n\r\n"; + + co_await awaiter; +} + +} // namespace clice::async::net diff --git a/src/Basic/SourceCode.cpp b/src/Basic/SourceCode.cpp index 490e8b90..77f9a657 100644 --- a/src/Basic/SourceCode.cpp +++ b/src/Basic/SourceCode.cpp @@ -1,4 +1,5 @@ #include "Basic/SourceCode.h" +#include "llvm/ADT/FunctionExtras.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" diff --git a/src/Basic/SourceConverter.cpp b/src/Basic/SourceConverter.cpp index 99f0657c..14bcab5d 100644 --- a/src/Basic/SourceConverter.cpp +++ b/src/Basic/SourceConverter.cpp @@ -1,10 +1,9 @@ #include "Basic/Location.h" #include "Basic/SourceCode.h" #include "Basic/SourceConverter.h" -#include "Support/Support.h" - +#include "Support/FileSystem.h" #include "clang/Basic/SourceManager.h" -#include "Compiler/Clang.h" +#include "llvm/ADT/StringExtras.h" namespace clice { diff --git a/src/Compiler/Command.cpp b/src/Compiler/Command.cpp index dd2d20fb..cd9f6bb4 100644 --- a/src/Compiler/Command.cpp +++ b/src/Compiler/Command.cpp @@ -1,12 +1,12 @@ #include "Compiler/Command.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" +#include "Support/FileSystem.h" +#include "Support/Format.h" namespace clice { -llvm::Error mangleCommand(llvm::StringRef command, - llvm::SmallVectorImpl& out, - llvm::SmallVectorImpl& buffer) { +std::expected mangleCommand(llvm::StringRef command, + llvm::SmallVectorImpl& out, + llvm::SmallVectorImpl& buffer) { llvm::SmallString<128> current; llvm::SmallVector indices; bool inSingleQuote = false; @@ -67,7 +67,7 @@ llvm::Error mangleCommand(llvm::StringRef command, out.push_back(arg.data()); } - return llvm::Error::success(); + return {}; } } // namespace clice diff --git a/src/Compiler/Compilation.cpp b/src/Compiler/Compilation.cpp index a9c283bb..af902cbe 100644 --- a/src/Compiler/Compilation.cpp +++ b/src/Compiler/Compilation.cpp @@ -12,7 +12,7 @@ std::unique_ptr createInvocation(CompilationParams& p llvm::SmallString<1024> buffer; llvm::SmallVector args; - if(auto error = mangleCommand(params.command, args, buffer)) { + if(auto result = mangleCommand(params.command, args, buffer); !result) { std::terminate(); } @@ -96,27 +96,27 @@ namespace { /// Execute given action with the on the given instance. `callback` is called after /// `BeginSourceFile`. Beacuse `BeginSourceFile` may create new preprocessor. -llvm::Error ExecuteAction(clang::CompilerInstance& instance, - clang::FrontendAction& action, - auto&& callback) { +std::expected ExecuteAction(clang::CompilerInstance& instance, + clang::FrontendAction& action, + auto&& callback) { if(!action.BeginSourceFile(instance, instance.getFrontendOpts().Inputs[0])) { - return error("Failed to begin source file"); + return std::unexpected("Failed to begin source file"); } callback(); if(auto error = action.Execute()) { - return error; + return std::unexpected(std::format("Failed to execute action, because {} ", error)); } - return llvm::Error::success(); + return {}; } -llvm::Expected ExecuteAction(std::unique_ptr instance, - std::unique_ptr action) { +std::expected ExecuteAction(std::unique_ptr instance, + std::unique_ptr action) { if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { - return error("Failed to begin source file"); + return std::unexpected("Failed to begin source file"); } auto& pp = instance->getPreprocessor(); @@ -139,7 +139,7 @@ llvm::Expected ExecuteAction(std::unique_ptr i } if(auto error = action->Execute()) { - return clice::error("Failed to execute action, because {} ", error); + return std::unexpected(std::format("Failed to execute action, because {} ", error)); } std::optional tokBuf; @@ -165,13 +165,14 @@ llvm::Expected ExecuteAction(std::unique_ptr i } // namespace -llvm::Expected compile(CompilationParams& params) { +std::expected compile(CompilationParams& params) { auto instance = impl::createInstance(params); return ExecuteAction(std::move(instance), std::make_unique()); } -llvm::Expected compile(CompilationParams& params, clang::CodeCompleteConsumer* consumer) { +std::expected compile(CompilationParams& params, + clang::CodeCompleteConsumer* consumer) { auto instance = impl::createInstance(params); /// Set options to run code completion. @@ -183,7 +184,7 @@ llvm::Expected compile(CompilationParams& params, clang::CodeCompleteCo return ExecuteAction(std::move(instance), std::make_unique()); } -llvm::Expected compile(CompilationParams& params, PCHInfo& out) { +std::expected compile(CompilationParams& params, PCHInfo& out) { assert(params.bound.has_value() && "Preamble bounds is required to build PCH"); auto instance = impl::createInstance(params); @@ -205,11 +206,11 @@ llvm::Expected compile(CompilationParams& params, PCHInfo& out) { return std::move(*info); } else { - return info.takeError(); + return std::unexpected(info.error()); } } -llvm::Expected compile(CompilationParams& params, PCMInfo& out) { +std::expected compile(CompilationParams& params, PCMInfo& out) { auto instance = impl::createInstance(params); /// Set options to generate PCM. @@ -230,7 +231,7 @@ llvm::Expected compile(CompilationParams& params, PCMInfo& out) { out.srcPath = params.srcPath.str(); return std::move(*info); } else { - return info.takeError(); + return std::unexpected(info.error()); } } diff --git a/src/Compiler/Diagnostic.cpp b/src/Compiler/Diagnostic.cpp index 3f4cbe7b..02bbf57b 100644 --- a/src/Compiler/Diagnostic.cpp +++ b/src/Compiler/Diagnostic.cpp @@ -1,30 +1,30 @@ -#include -#include - -#include -#include - -// #include +#include "Compiler/Diagnostic.h" +#include "clang/AST/Type.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/AllDiagnostics.h" namespace clice { -void DiagnosticCollector::BeginSourceFile(const clang::LangOptions& Opts, const clang::Preprocessor* PP) { +void DiagnosticCollector::BeginSourceFile(const clang::LangOptions& Opts, + const clang::Preprocessor* PP) { }; const char* getDiagnosticCode(unsigned ID) { switch(ID) { -#define DIAG(ENUM, \ - CLASS, \ - DEFAULT_MAPPING, \ - DESC, \ - GROPU, \ - SFINAE, \ - NOWERROR, \ - SHOWINSYSHEADER, \ - SHOWINSYSMACRO, \ - DEFERRABLE, \ - CATEGORY) \ +#define DIAG(ENUM, \ + CLASS, \ + DEFAULT_MAPPING, \ + DESC, \ + GROPU, \ + SFINAE, \ + NOWERROR, \ + SHOWINSYSHEADER, \ + SHOWINSYSMACRO, \ + DEFERRABLE, \ + CATEGORY) \ case clang::diag::ENUM: return #ENUM; #include "clang/Basic/DiagnosticASTKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" @@ -58,15 +58,18 @@ void dumpArg(clang::DiagnosticsEngine::ArgumentKind kind, std::uint64_t value) { } case clang::DiagnosticsEngine::ak_qualtype: { - clang::QualType type = clang::QualType::getFromOpaquePtr(reinterpret_cast(value)); + clang::QualType type = + clang::QualType::getFromOpaquePtr(reinterpret_cast(value)); llvm::outs() << type.getAsString(); break; } case clang::DiagnosticsEngine::ak_qualtype_pair: { clang::TemplateDiffTypes& TDT = *reinterpret_cast(value); - clang::QualType type1 = clang::QualType::getFromOpaquePtr(reinterpret_cast(TDT.FromType)); - clang::QualType type2 = clang::QualType::getFromOpaquePtr(reinterpret_cast(TDT.ToType)); + clang::QualType type1 = + clang::QualType::getFromOpaquePtr(reinterpret_cast(TDT.FromType)); + clang::QualType type2 = + clang::QualType::getFromOpaquePtr(reinterpret_cast(TDT.ToType)); llvm::outs() << type1.getAsString() << " -> " << type2.getAsString(); break; } @@ -109,7 +112,8 @@ void dumpArg(clang::DiagnosticsEngine::ArgumentKind kind, std::uint64_t value) { llvm::outs() << "\n"; } -void DiagnosticCollector::HandleDiagnostic(clang::DiagnosticsEngine::Level level, const clang::Diagnostic& diagnostic) { +void DiagnosticCollector::HandleDiagnostic(clang::DiagnosticsEngine::Level level, + const clang::Diagnostic& diagnostic) { llvm::SmallString<128> message; diagnostic.FormatDiagnostic(message); // diagnostic.getLocation(); @@ -126,8 +130,9 @@ void DiagnosticCollector::HandleDiagnostic(clang::DiagnosticsEngine::Level level // dumpArg(diagnostic.getArgKind(0), diagnostic.getRawArg(0)); // FIXME: - // use DiagnosticEngine::SetArgToStringFn to set a custom function to convert arguments to strings. - // Support markdown diagnostic in LSP 3.18. allow complex type to display in markdown code block. + // use DiagnosticEngine::SetArgToStringFn to set a custom function to convert arguments to + // strings. Support markdown diagnostic in LSP 3.18. allow complex type to display in markdown + // code block. }; void DiagnosticCollector::EndSourceFile() { diff --git a/src/Compiler/Directive.cpp b/src/Compiler/Directive.cpp index c32e3224..7dd9d89a 100644 --- a/src/Compiler/Directive.cpp +++ b/src/Compiler/Directive.cpp @@ -1,7 +1,7 @@ -#include -#include -#include -#include +#include "Compiler/Directive.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/MacroArgs.h" +#include "clang/Lex/Preprocessor.h" namespace clice { diff --git a/src/Compiler/Module.cpp b/src/Compiler/Module.cpp index 67e6e3ca..f9c604f2 100644 --- a/src/Compiler/Module.cpp +++ b/src/Compiler/Module.cpp @@ -96,7 +96,7 @@ std::string scanModuleName(CompilationParams& params) { return info->name; } -llvm::Expected scanModule(CompilationParams& params) { +std::expected scanModule(CompilationParams& params) { struct ModuleCollector : public clang::PPCallbacks { ModuleInfo& info; @@ -115,7 +115,7 @@ llvm::Expected scanModule(CompilationParams& params) { auto instance = impl::createInstance(params); if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { - return error("Failed to begin source file"); + return std::unexpected("Failed to begin source file"); } auto& pp = instance->getPreprocessor(); @@ -123,7 +123,7 @@ llvm::Expected scanModule(CompilationParams& params) { pp.addPPCallbacks(std::make_unique(info)); if(auto error = action.Execute()) { - return error; + return std::unexpected(std::format("{}", error)); } if(pp.isInNamedModule()) { diff --git a/src/Driver/clice.cc b/src/Driver/clice.cc index 040098e3..a82d04c9 100644 --- a/src/Driver/clice.cc +++ b/src/Driver/clice.cc @@ -1,4 +1,4 @@ -#include "Server/Logger.h" +#include "Support/Logger.h" #include "Server/Server.h" #include "llvm/Support/CommandLine.h" @@ -47,9 +47,9 @@ int main(int argc, const char** argv) { }; if(cl::pipe && cl::pipe.getValue()) { - async::listen(loop); + async::net::listen(loop); } else { - async::listen(loop, "127.0.0.1", 50051); + async::net::listen("127.0.0.1", 50051, loop); } async::run(); diff --git a/src/Driver/integration_tests.cc b/src/Driver/integration_tests.cc index 422441ce..172f2be9 100644 --- a/src/Driver/integration_tests.cc +++ b/src/Driver/integration_tests.cc @@ -1,6 +1,5 @@ #include "Async/Async.h" -#include "Server/Logger.h" -#include "Support/Support.h" +#include "Support/Logger.h" #include "llvm/Support/CommandLine.h" using namespace clice; @@ -37,7 +36,7 @@ async::Task request(llvm::StringRef dir, llvm::StringRef file, llvm::String log::info("Send Request: {0}", method); - co_await async::write(std::move(request)); + co_await async::net::write(std::move(request)); co_return 1; } @@ -46,13 +45,12 @@ int main(int argc, const char** argv) { llvm::cl::SetVersionPrinter([](llvm::raw_ostream& os) { os << "clice version: 0.0.1\n"; }); llvm::cl::ParseCommandLineOptions(argc, argv, "clice language server"); - async::spawn( - [](json::Value value) -> async::Task<> { - print("Receive: {0}", value); - co_return; - }, - cl::execute.getValue(), - {"--pipe=true", "--config=/home/ykiko/C++/clice2/docs/clice.toml"}); + async::net::spawn(cl::execute.getValue(), + {"--pipe=true", "--config=/home/ykiko/C++/clice2/docs/clice.toml"}, + [](json::Value value) -> async::Task<> { + print("Receive: {0}", value); + co_return; + }); auto p = request("initialize", "input.json", "initialize"); async::run(p); diff --git a/src/Driver/unit_tests.cc b/src/Driver/unit_tests.cc index ec0f9d29..d3551872 100644 --- a/src/Driver/unit_tests.cc +++ b/src/Driver/unit_tests.cc @@ -1,7 +1,6 @@ #include "Test/Test.h" -#include "llvm/Support/CommandLine.h" #include "llvm/ADT/SmallString.h" -#include "Support/Support.h" +#include "llvm/Support/CommandLine.h" namespace clice { diff --git a/src/Feature/CodeCompletion.cpp b/src/Feature/CodeCompletion.cpp index 6d3a040b..96f051e6 100644 --- a/src/Feature/CodeCompletion.cpp +++ b/src/Feature/CodeCompletion.cpp @@ -1,6 +1,8 @@ -#include -#include -#include +#include "AST/SymbolKind.h" +#include "Basic/SourceConverter.h" +#include "Compiler/Compilation.h" +#include "Feature/CodeCompletion.h" +#include "clang/Sema/CodeCompleteConsumer.h" namespace clice::feature { @@ -130,41 +132,41 @@ public: clang::CodeCompletionContext context, clang::CodeCompletionResult* results, unsigned count) final { - auto loc = sema.getPreprocessor().getCodeCompletionLoc(); - auto offset = sema.getSourceManager().getFileOffset(loc); - auto prefix = CompletionPrefix::from(content, offset); - - for(auto& result: llvm::make_range(results, results + count)) { - auto& item = completions.emplace_back(); - item.kind = proto::CompletionItemKind::Text; - switch(result.Kind) { - case clang::CodeCompletionResult::RK_Declaration: { - item.label = getName(result.Declaration); - item.kind = kindForDecl(result.Declaration); - item.detail = result.Declaration->getNameAsString(); - break; - } - case clang::CodeCompletionResult::RK_Keyword: { - item.label = result.Keyword; - item.kind = proto::CompletionItemKind::Keyword; - break; - } - case clang::CodeCompletionResult::RK_Macro: { - item.label = result.Macro->getName(); - break; - } - case clang::CodeCompletionResult::RK_Pattern: { - item.kind = proto::CompletionItemKind::Snippet; - item.label = result.Pattern->getTypedText(); - break; - } - } - item.textEdit.newText = item.label; - item.textEdit.range = { - .start = {line - 1, static_cast(column - 1 - prefix.name.size())}, - .end = {line - 1, static_cast(column + item.label.size()) - 1 }, - }; - } + // auto loc = sema.getPreprocessor().getCodeCompletionLoc(); + // auto offset = sema.getSourceManager().getFileOffset(loc); + // auto prefix = CompletionPrefix::from(content, offset); + // + // for(auto& result: llvm::make_range(results, results + count)) { + // auto& item = completions.emplace_back(); + // item.kind = proto::CompletionItemKind::Text; + // switch(result.Kind) { + // case clang::CodeCompletionResult::RK_Declaration: { + // item.label = getName(result.Declaration); + // item.kind = kindForDecl(result.Declaration); + // item.detail = result.Declaration->getNameAsString(); + // break; + // } + // case clang::CodeCompletionResult::RK_Keyword: { + // item.label = result.Keyword; + // item.kind = proto::CompletionItemKind::Keyword; + // break; + // } + // case clang::CodeCompletionResult::RK_Macro: { + // item.label = result.Macro->getName(); + // break; + // } + // case clang::CodeCompletionResult::RK_Pattern: { + // item.kind = proto::CompletionItemKind::Snippet; + // item.label = result.Pattern->getTypedText(); + // break; + // } + // } + // item.textEdit.newText = item.label; + // item.textEdit.range = { + // .start = {line - 1, static_cast(column - 1 - prefix.name.size())}, + // .end = {line - 1, static_cast(column + item.label.size()) - 1 }, + // }; + //} } clang::CodeCompletionAllocator& getAllocator() final { diff --git a/src/Feature/DocumentSymbol.cpp b/src/Feature/DocumentSymbol.cpp index 507ddd49..1eb56ad4 100644 --- a/src/Feature/DocumentSymbol.cpp +++ b/src/Feature/DocumentSymbol.cpp @@ -1,6 +1,7 @@ #include "Basic/SourceConverter.h" #include "Feature/DocumentSymbol.h" #include "Compiler/Compilation.h" +#include "clang/AST/RecursiveASTVisitor.h" namespace clice { diff --git a/src/Feature/FoldingRange.cpp b/src/Feature/FoldingRange.cpp index 47a0fdd5..f05de9c0 100644 --- a/src/Feature/FoldingRange.cpp +++ b/src/Feature/FoldingRange.cpp @@ -1,6 +1,7 @@ #include "Feature/FoldingRange.h" #include "Compiler/Compilation.h" #include "Index/Shared.h" +#include "clang/AST/RecursiveASTVisitor.h" /// Clangd's FoldingRange Implementation: /// https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clangd/SemanticSelection.cpp diff --git a/src/Feature/InlayHint.cpp b/src/Feature/InlayHint.cpp index a6c337cb..cbee0950 100644 --- a/src/Feature/InlayHint.cpp +++ b/src/Feature/InlayHint.cpp @@ -1,6 +1,7 @@ #include "Basic/SourceConverter.h" #include "Compiler/Compilation.h" #include "Feature/InlayHint.h" +#include "clang/AST/RecursiveASTVisitor.h" namespace clice { diff --git a/src/Feature/SemanticTokens.cpp b/src/Feature/SemanticTokens.cpp index 2bf1e864..a952a995 100644 --- a/src/Feature/SemanticTokens.cpp +++ b/src/Feature/SemanticTokens.cpp @@ -1,6 +1,7 @@ +#include "AST/Semantic.h" #include "Index/Shared.h" -#include "Compiler/Semantic.h" #include "Feature/SemanticTokens.h" +#include "Support/Compare.h" namespace clice::feature { diff --git a/src/Index/SymbolIndex.cpp b/src/Index/SymbolIndex.cpp index 58702b6b..a5bbc347 100644 --- a/src/Index/SymbolIndex.cpp +++ b/src/Index/SymbolIndex.cpp @@ -2,11 +2,11 @@ #include "Index/Index.h" +#include "AST/Semantic.h" #include "Basic/SourceCode.h" #include "Index/SymbolIndex.h" -#include "Compiler/Semantic.h" #include "Support/Binary.h" - +#include "Support/Compare.h" #include "clang/Index/USRGeneration.h" namespace clice::index { diff --git a/src/Index/USRGeneration.cpp b/src/Index/USRGeneration.cpp index 236b8c14..0c7dbaf8 100644 --- a/src/Index/USRGeneration.cpp +++ b/src/Index/USRGeneration.cpp @@ -1,64 +1,920 @@ +#include "Index/USR.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" +#include "clang/AST/ODRHash.h" +#include "clang/Basic/SourceManager.h" -namespace clice::index { +using namespace clang; + +/// \returns true on error. +static bool printLoc(llvm::raw_ostream& OS, + SourceLocation Loc, + const SourceManager& SM, + bool IncludeOffset) { + if(Loc.isInvalid()) { + return true; + } + Loc = SM.getExpansionLoc(Loc); + const std::pair& Decomposed = SM.getDecomposedLoc(Loc); + OptionalFileEntryRef FE = SM.getFileEntryRefForID(Decomposed.first); + if(FE) { + OS << llvm::sys::path::filename(FE->getName()); + } else { + // This case really isn't interesting. + return true; + } + if(IncludeOffset) { + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + OS << '@' << Decomposed.second; + } + return false; +} + +static StringRef GetExternalSourceContainer(const NamedDecl* D) { + if(!D) + return StringRef(); + if(auto* attr = D->getExternalSourceSymbolAttr()) { + return attr->getDefinedIn(); + } + return StringRef(); +} namespace { +class USRGenerator : public ConstDeclVisitor { + SmallVectorImpl& Buf; + llvm::raw_svector_ostream Out; + ASTContext* Context; + const LangOptions& LangOpts; + bool IgnoreResults = false; + bool generatedLoc = false; + + llvm::DenseMap TypeSubstitutions; -class USRGenerator : public clang::ConstDeclVisitor { public: - USRGenerator(llvm::SmallVectorImpl& buffer, - const clang::ASTContext& Ctx, - const clang::SourceManager& SM) : buffer(buffer), Ctx(Ctx), SM(SM) {} + USRGenerator(ASTContext* Ctx, SmallVectorImpl& Buf, const LangOptions& LangOpts) : + Buf(Buf), Out(Buf), Context(Ctx), LangOpts(LangOpts) { + // Add the USR space prefix. + Out << "c:"; + } - void VisitDeclContext(const clang::DeclContext* DC) { - if(auto ND = llvm::dyn_cast(DC)) { - Visit(ND); - } else if(auto LSD = llvm::dyn_cast(DC)) { - VisitDeclContext(DC->getParent()); + bool ignoreResults() const { + return IgnoreResults; + } + + // Visitation methods from generating USRs from AST elements. + void VisitDeclContext(const DeclContext* D); + void VisitFieldDecl(const FieldDecl* D); + void VisitFunctionDecl(const FunctionDecl* D); + void VisitNamedDecl(const NamedDecl* D); + void VisitNamespaceDecl(const NamespaceDecl* D); + void VisitNamespaceAliasDecl(const NamespaceAliasDecl* D); + void VisitFunctionTemplateDecl(const FunctionTemplateDecl* D); + void VisitClassTemplateDecl(const ClassTemplateDecl* D); + void VisitTagDecl(const TagDecl* D); + void VisitTypedefDecl(const TypedefDecl* D); + void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl* D); + void VisitVarDecl(const VarDecl* D); + void VisitBindingDecl(const BindingDecl* D); + void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl* D); + void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl* D); + void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl* D); + void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl* D); + void VisitConceptDecl(const ConceptDecl* D); + + void VisitLinkageSpecDecl(const LinkageSpecDecl* D) { + IgnoreResults = true; // No USRs for linkage specs themselves. + } + + void VisitUsingDirectiveDecl(const UsingDirectiveDecl* D) { + IgnoreResults = true; + } + + void VisitUsingDecl(const UsingDecl* D) { + VisitDeclContext(D->getDeclContext()); + Out << "@UD@"; + + bool EmittedDeclName = !EmitDeclName(D); + assert(EmittedDeclName && "EmitDeclName can not fail for UsingDecls"); + (void)EmittedDeclName; + } + + bool ShouldGenerateLocation(const NamedDecl* D); + + bool isLocal(const NamedDecl* D) { + return D->getParentFunctionOrMethod() != nullptr; + } + + void GenExtSymbolContainer(const NamedDecl* D); + + /// Generate the string component containing the location of the + /// declaration. + bool GenLoc(const Decl* D, bool IncludeOffset); + + /// String generation methods used both by the visitation methods + /// and from other clients that want to directly generate USRs. These + /// methods do not construct complete USRs (which incorporate the parents + /// of an AST element), but only the fragments concerning the AST element + /// itself. + + void VisitType(QualType T); + void VisitTemplateParameterList(const TemplateParameterList* Params); + void VisitTemplateName(TemplateName Name); + void VisitTemplateArgument(const TemplateArgument& Arg); + + void VisitMSGuidDecl(const MSGuidDecl* D); + + /// Emit a Decl's name using NamedDecl::printName() and return true if + /// the decl had no name. + bool EmitDeclName(const NamedDecl* D); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Generating USRs from ASTS. +//===----------------------------------------------------------------------===// + +bool USRGenerator::EmitDeclName(const NamedDecl* D) { + DeclarationName N = D->getDeclName(); + if(N.isEmpty()) + return true; + Out << N; + return false; +} + +bool USRGenerator::ShouldGenerateLocation(const NamedDecl* D) { + if(D->isExternallyVisible()) + return false; + if(D->getParentFunctionOrMethod()) + return true; + SourceLocation Loc = D->getLocation(); + if(Loc.isInvalid()) + return false; + const SourceManager& SM = Context->getSourceManager(); + return !SM.isInSystemHeader(Loc); +} + +void USRGenerator::VisitDeclContext(const DeclContext* DC) { + if(const NamedDecl* D = dyn_cast(DC)) + Visit(D); + else if(isa(DC)) // Linkage specs are transparent in USRs. + VisitDeclContext(DC->getParent()); +} + +void USRGenerator::VisitFieldDecl(const FieldDecl* D) { + // The USR for an ivar declared in a class extension is based on the + // ObjCInterfaceDecl, not the ObjCCategoryDecl. + if(const ObjCInterfaceDecl* ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + VisitDeclContext(D->getDeclContext()); + Out << (isa(D) ? "@" : "@FI@"); + if(EmitDeclName(D)) { + // Bit fields can be anonymous. + IgnoreResults = true; + return; + } +} + +void USRGenerator::VisitFunctionDecl(const FunctionDecl* D) { + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + + if(D->getType().isNull()) { + IgnoreResults = true; + return; + } + + const unsigned StartSize = Buf.size(); + VisitDeclContext(D->getDeclContext()); + if(Buf.size() == StartSize) + GenExtSymbolContainer(D); + + bool IsTemplate = false; + if(FunctionTemplateDecl* FunTmpl = D->getDescribedFunctionTemplate()) { + IsTemplate = true; + Out << "@FT@"; + VisitTemplateParameterList(FunTmpl->getTemplateParameters()); + } else + Out << "@F@"; + + PrintingPolicy Policy(LangOpts); + // Forward references can have different template argument names. Suppress the + // template argument names in constructors to make their USR more stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + D->getDeclName().print(Out, Policy); + + if((!LangOpts.CPlusPlus || D->isExternC()) && !D->hasAttr()) + return; + + if(D->isFunctionTemplateSpecialization()) { + Out << '<'; + if(const TemplateArgumentList* SpecArgs = D->getTemplateSpecializationArgs()) { + for(const auto& Arg: SpecArgs->asArray()) { + Out << '#'; + VisitTemplateArgument(Arg); + } + } else if(const ASTTemplateArgumentListInfo* SpecArgsWritten = + D->getTemplateSpecializationArgsAsWritten()) { + for(const auto& ArgLoc: SpecArgsWritten->arguments()) { + Out << '#'; + VisitTemplateArgument(ArgLoc.getArgument()); + } + } + Out << '>'; + } + + QualType CanonicalType = D->getType().getCanonicalType(); + // Mangle in type information for the arguments. + if(const auto* FPT = CanonicalType->getAs()) { + for(QualType PT: FPT->param_types()) { + Out << '#'; + VisitType(PT); + } + } + if(D->isVariadic()) + Out << '.'; + if(IsTemplate) { + // Function templates can be overloaded by return type, for example: + // \code + // template typename T::A foo() {} + // template typename T::B foo() {} + // \endcode + Out << '#'; + VisitType(D->getReturnType()); + } + Out << '#'; + if(const CXXMethodDecl* MD = dyn_cast(D)) { + if(MD->isStatic()) + Out << 'S'; + // FIXME: OpenCL: Need to consider address spaces + if(unsigned quals = MD->getMethodQualifiers().getCVRUQualifiers()) + Out << (char)('0' + quals); + switch(MD->getRefQualifier()) { + case RQ_None: break; + case RQ_LValue: Out << '&'; break; + case RQ_RValue: Out << "&&"; break; + } + } +} + +void USRGenerator::VisitNamedDecl(const NamedDecl* D) { + VisitDeclContext(D->getDeclContext()); + Out << "@"; + + if(EmitDeclName(D)) { + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, + // e.g.: void (*f)(void *); + // In this case, don't generate a USR. + IgnoreResults = true; + } +} + +void USRGenerator::VisitVarDecl(const VarDecl* D) { + // VarDecls can be declared 'extern' within a function or method body, + // but their enclosing DeclContext is the function, not the TU. We need + // to check the storage class to correctly generate the USR. + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + + VisitDeclContext(D->getDeclContext()); + + if(VarTemplateDecl* VarTmpl = D->getDescribedVarTemplate()) { + Out << "@VT"; + VisitTemplateParameterList(VarTmpl->getTemplateParameters()); + } else if(const VarTemplatePartialSpecializationDecl* PartialSpec = + dyn_cast(D)) { + Out << "@VP"; + VisitTemplateParameterList(PartialSpec->getTemplateParameters()); + } + + // Variables always have simple names. + StringRef s = D->getName(); + + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if(s.empty()) + IgnoreResults = true; + else + Out << '@' << s; + + // For a template specialization, mangle the template arguments. + if(const VarTemplateSpecializationDecl* Spec = dyn_cast(D)) { + const TemplateArgumentList& Args = Spec->getTemplateArgs(); + Out << '>'; + for(unsigned I = 0, N = Args.size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(Args.get(I)); + } + } +} + +void USRGenerator::VisitBindingDecl(const BindingDecl* D) { + if(isLocal(D) && GenLoc(D, /*IncludeOffset=*/true)) + return; + VisitNamedDecl(D); +} + +void USRGenerator::VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl* D) { + GenLoc(D, /*IncludeOffset=*/true); +} + +void USRGenerator::VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl* D) { + GenLoc(D, /*IncludeOffset=*/true); +} + +void USRGenerator::VisitNamespaceDecl(const NamespaceDecl* D) { + if(IgnoreResults) + return; + VisitDeclContext(D->getDeclContext()); + if(D->isAnonymousNamespace()) { + Out << "@aN"; + return; + } + Out << "@N@" << D->getName(); +} + +void USRGenerator::VisitFunctionTemplateDecl(const FunctionTemplateDecl* D) { + VisitFunctionDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitClassTemplateDecl(const ClassTemplateDecl* D) { + VisitTagDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitNamespaceAliasDecl(const NamespaceAliasDecl* D) { + VisitDeclContext(D->getDeclContext()); + if(!IgnoreResults) + Out << "@NA@" << D->getName(); +} + +const static ObjCCategoryDecl* getCategoryContext(const NamedDecl* D) { + if(auto* CD = dyn_cast(D->getDeclContext())) + return CD; + if(auto* ICD = dyn_cast(D->getDeclContext())) + return ICD->getCategoryDecl(); + return nullptr; +} + +void USRGenerator::VisitTagDecl(const TagDecl* D) { + // Add the location of the tag decl to handle resolution across + // translation units. + if(!isa(D) && ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + + GenExtSymbolContainer(D); + + D = D->getCanonicalDecl(); + VisitDeclContext(D->getDeclContext()); + + bool AlreadyStarted = false; + if(const CXXRecordDecl* CXXRecord = dyn_cast(D)) { + if(ClassTemplateDecl* ClassTmpl = CXXRecord->getDescribedClassTemplate()) { + AlreadyStarted = true; + + switch(D->getTagKind()) { + case TagTypeKind::Interface: + case TagTypeKind::Class: + case TagTypeKind::Struct: Out << "@ST"; break; + case TagTypeKind::Union: Out << "@UT"; break; + case TagTypeKind::Enum: llvm_unreachable("enum template"); + } + VisitTemplateParameterList(ClassTmpl->getTemplateParameters()); + } else if(const ClassTemplatePartialSpecializationDecl* PartialSpec = + dyn_cast(CXXRecord)) { + AlreadyStarted = true; + + switch(D->getTagKind()) { + case TagTypeKind::Interface: + case TagTypeKind::Class: + case TagTypeKind::Struct: Out << "@SP"; break; + case TagTypeKind::Union: Out << "@UP"; break; + case TagTypeKind::Enum: llvm_unreachable("enum partial specialization"); + } + VisitTemplateParameterList(PartialSpec->getTemplateParameters()); } } -#define VISIT_DECL(Type) void Visit##Type(const clang::Type* decl) + if(!AlreadyStarted) { + switch(D->getTagKind()) { + case TagTypeKind::Interface: + case TagTypeKind::Class: + case TagTypeKind::Struct: Out << "@S"; break; + case TagTypeKind::Union: Out << "@U"; break; + case TagTypeKind::Enum: Out << "@E"; break; + } + } - VISIT_DECL(NamespaceDecl) {} + Out << '@'; + assert(Buf.size() > 0); + const unsigned off = Buf.size() - 1; - VISIT_DECL(NamespaceAliasDecl) {} + if(EmitDeclName(D)) { + if(const TypedefNameDecl* TD = D->getTypedefNameForAnonDecl()) { + Buf[off] = 'A'; + Out << '@' << *TD; + } else { + if(D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { + printLoc(Out, D->getLocation(), Context->getSourceManager(), true); + } else { + Buf[off] = 'a'; + if(auto* ED = dyn_cast(D)) { + // Distinguish USRs of anonymous enums by using their first + // enumerator. + auto enum_range = ED->enumerators(); + if(enum_range.begin() != enum_range.end()) { + Out << '@' << **enum_range.begin(); + } + } + } + } + } - VISIT_DECL(FieldDecl) {} + // For a class template specialization, mangle the template arguments. + if(const ClassTemplateSpecializationDecl* Spec = dyn_cast(D)) { + const TemplateArgumentList& Args = Spec->getTemplateArgs(); + Out << '>'; + for(unsigned I = 0, N = Args.size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(Args.get(I)); + } + } +} - VISIT_DECL(EnumConstantDecl) {} +void USRGenerator::VisitTypedefDecl(const TypedefDecl* D) { + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + const DeclContext* DC = D->getDeclContext(); + if(const NamedDecl* DCN = dyn_cast(DC)) + Visit(DCN); + Out << "@T@"; + Out << D->getName(); +} - VISIT_DECL(VarDecl) {} +void USRGenerator::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl* D) { + GenLoc(D, /*IncludeOffset=*/true); +} - VISIT_DECL(VarTemplateDecl) {} +void USRGenerator::GenExtSymbolContainer(const NamedDecl* D) { + StringRef Container = GetExternalSourceContainer(D); + if(!Container.empty()) + Out << "@M@" << Container; +} - VISIT_DECL(VarTemplateSpecializationDecl) {} +bool USRGenerator::GenLoc(const Decl* D, bool IncludeOffset) { + if(generatedLoc) + return IgnoreResults; + generatedLoc = true; - VISIT_DECL(VarTemplatePartialSpecializationDecl) {} + // Guard against null declarations in invalid code. + if(!D) { + IgnoreResults = true; + return true; + } - VISIT_DECL(FunctionDecl) {} + // Use the location of canonical decl. + D = D->getCanonicalDecl(); - VISIT_DECL(FunctionTemplateDecl) {} + IgnoreResults = IgnoreResults || + printLoc(Out, D->getBeginLoc(), Context->getSourceManager(), IncludeOffset); - VISIT_DECL(RecordDecl) {} + return IgnoreResults; +} - VISIT_DECL(CXXRecordDecl) {} +static void printQualifier(llvm::raw_ostream& Out, + const LangOptions& LangOpts, + NestedNameSpecifier* NNS) { + // FIXME: Encode the qualifier, don't just print it. + PrintingPolicy PO(LangOpts); + PO.SuppressTagKeyword = true; + PO.SuppressUnwrittenScope = true; + PO.ConstantArraySizeAsWritten = false; + PO.AnonymousTagLocations = false; + NNS->print(Out, PO); +} - VISIT_DECL(ClassTemplateDecl) {} +void USRGenerator::VisitType(QualType T) { + // This method mangles in USR information for types. It can possibly + // just reuse the naming-mangling logic used by codegen, although the + // requirements for USRs might not be the same. + ASTContext& Ctx = *Context; - VISIT_DECL(ClassTemplateSpecializationDecl) {} + do { + T = Ctx.getCanonicalType(T); + Qualifiers Q = T.getQualifiers(); + unsigned qVal = 0; + if(Q.hasConst()) + qVal |= 0x1; + if(Q.hasVolatile()) + qVal |= 0x2; + if(Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + Out << ((char)('0' + qVal)); - VISIT_DECL(ClassTemplatePartialSpecializationDecl) {} + // Mangle in ObjC GC qualifiers? -#undef VISIT_DECL + if(const PackExpansionType* Expansion = T->getAs()) { + Out << 'P'; + T = Expansion->getPattern(); + } -private: - llvm::SmallVectorImpl& buffer; + if(const BuiltinType* BT = T->getAs()) { + switch(BT->getKind()) { + case BuiltinType::Void: Out << 'v'; break; + case BuiltinType::Bool: Out << 'b'; break; + case BuiltinType::UChar: Out << 'c'; break; + case BuiltinType::Char8: Out << 'u'; break; + case BuiltinType::Char16: Out << 'q'; break; + case BuiltinType::Char32: Out << 'w'; break; + case BuiltinType::UShort: Out << 's'; break; + case BuiltinType::UInt: Out << 'i'; break; + case BuiltinType::ULong: Out << 'l'; break; + case BuiltinType::ULongLong: Out << 'k'; break; + case BuiltinType::UInt128: Out << 'j'; break; + case BuiltinType::Char_U: + case BuiltinType::Char_S: Out << 'C'; break; + case BuiltinType::SChar: Out << 'r'; break; + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: Out << 'W'; break; + case BuiltinType::Short: Out << 'S'; break; + case BuiltinType::Int: Out << 'I'; break; + case BuiltinType::Long: Out << 'L'; break; + case BuiltinType::LongLong: Out << 'K'; break; + case BuiltinType::Int128: Out << 'J'; break; + case BuiltinType::Float16: + case BuiltinType::Half: Out << 'h'; break; + case BuiltinType::Float: Out << 'f'; break; + case BuiltinType::Double: Out << 'd'; break; + case BuiltinType::LongDouble: Out << 'D'; break; + case BuiltinType::Float128: Out << 'Q'; break; + case BuiltinType::NullPtr: Out << 'n'; break; +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: Out << "@BT@" << #Suffix << "_" << #ImgType; break; +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: Out << "@BT@" << #ExtType; break; +#include "clang/Basic/OpenCLExtensionTypes.def" + case BuiltinType::OCLEvent: Out << "@BT@OCLEvent"; break; + case BuiltinType::OCLClkEvent: Out << "@BT@OCLClkEvent"; break; + case BuiltinType::OCLQueue: Out << "@BT@OCLQueue"; break; + case BuiltinType::OCLReserveID: Out << "@BT@OCLReserveID"; break; + case BuiltinType::OCLSampler: Out << "@BT@OCLSampler"; break; +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: Out << "@BT@" << Name; break; +#include "clang/Basic/AArch64SVEACLETypes.def" +#define PPC_VECTOR_TYPE(Name, Id, Size) \ + case BuiltinType::Id: Out << "@BT@" << #Name; break; +#include "clang/Basic/PPCTypes.def" +#define RVV_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: Out << "@BT@" << Name; break; +#include "clang/Basic/RISCVVTypes.def" +#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/WebAssemblyReferenceTypes.def" +#define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) \ + case BuiltinType::Id: Out << "@BT@" << #Name; break; +#include "clang/Basic/AMDGPUTypes.def" +#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: Out << "@BT@" << #Name; break; +#include "clang/Basic/HLSLIntangibleTypes.def" + case BuiltinType::ShortAccum: Out << "@BT@ShortAccum"; break; + case BuiltinType::Accum: Out << "@BT@Accum"; break; + case BuiltinType::LongAccum: Out << "@BT@LongAccum"; break; + case BuiltinType::UShortAccum: Out << "@BT@UShortAccum"; break; + case BuiltinType::UAccum: Out << "@BT@UAccum"; break; + case BuiltinType::ULongAccum: Out << "@BT@ULongAccum"; break; + case BuiltinType::ShortFract: Out << "@BT@ShortFract"; break; + case BuiltinType::Fract: Out << "@BT@Fract"; break; + case BuiltinType::LongFract: Out << "@BT@LongFract"; break; + case BuiltinType::UShortFract: Out << "@BT@UShortFract"; break; + case BuiltinType::UFract: Out << "@BT@UFract"; break; + case BuiltinType::ULongFract: Out << "@BT@ULongFract"; break; + case BuiltinType::SatShortAccum: Out << "@BT@SatShortAccum"; break; + case BuiltinType::SatAccum: Out << "@BT@SatAccum"; break; + case BuiltinType::SatLongAccum: Out << "@BT@SatLongAccum"; break; + case BuiltinType::SatUShortAccum: Out << "@BT@SatUShortAccum"; break; + case BuiltinType::SatUAccum: Out << "@BT@SatUAccum"; break; + case BuiltinType::SatULongAccum: Out << "@BT@SatULongAccum"; break; + case BuiltinType::SatShortFract: Out << "@BT@SatShortFract"; break; + case BuiltinType::SatFract: Out << "@BT@SatFract"; break; + case BuiltinType::SatLongFract: Out << "@BT@SatLongFract"; break; + case BuiltinType::SatUShortFract: Out << "@BT@SatUShortFract"; break; + case BuiltinType::SatUFract: Out << "@BT@SatUFract"; break; + case BuiltinType::SatULongFract: Out << "@BT@SatULongFract"; break; + case BuiltinType::BFloat16: Out << "@BT@__bf16"; break; + case BuiltinType::Ibm128: Out << "@BT@__ibm128"; break; + case BuiltinType::ObjCId: Out << 'o'; break; + case BuiltinType::ObjCClass: Out << 'O'; break; + case BuiltinType::ObjCSel: Out << 'e'; break; +#define BUILTIN_TYPE(Id, SingletonId) +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + case BuiltinType::Dependent: + // If you're adding a new builtin type, please add its name prefixed + // with "@BT@" to `Out` (see cases above). + IgnoreResults = true; + break; + } + return; + } - const clang::ASTContext& Ctx; - const clang::SourceManager& SM; -}; + // If we have already seen this (non-built-in) type, use a substitution + // encoding. + llvm::DenseMap::iterator Substitution = + TypeSubstitutions.find(T.getTypePtr()); + if(Substitution != TypeSubstitutions.end()) { + Out << 'S' << Substitution->second << '_'; + return; + } else { + // Record this as a substitution. + unsigned Number = TypeSubstitutions.size(); + TypeSubstitutions[T.getTypePtr()] = Number; + } -} // namespace + if(const PointerType* PT = T->getAs()) { + Out << '*'; + T = PT->getPointeeType(); + continue; + } + if(const ObjCObjectPointerType* OPT = T->getAs()) { + Out << '*'; + T = OPT->getPointeeType(); + continue; + } + if(const RValueReferenceType* RT = T->getAs()) { + Out << "&&"; + T = RT->getPointeeType(); + continue; + } + if(const ReferenceType* RT = T->getAs()) { + Out << '&'; + T = RT->getPointeeType(); + continue; + } + if(const FunctionProtoType* FT = T->getAs()) { + Out << 'F'; + VisitType(FT->getReturnType()); + Out << '('; + for(const auto& I: FT->param_types()) { + Out << '#'; + VisitType(I); + } + Out << ')'; + if(FT->isVariadic()) + Out << '.'; + return; + } + if(const BlockPointerType* BT = T->getAs()) { + Out << 'B'; + T = BT->getPointeeType(); + continue; + } + if(const ComplexType* CT = T->getAs()) { + Out << '<'; + T = CT->getElementType(); + continue; + } + if(const TagType* TT = T->getAs()) { + Out << '$'; + VisitTagDecl(TT->getDecl()); + return; + } + if(const ObjCInterfaceType* OIT = T->getAs()) { + Out << '$'; + VisitObjCInterfaceDecl(OIT->getDecl()); + return; + } + if(const ObjCObjectType* OIT = T->getAs()) { + Out << 'Q'; + VisitType(OIT->getBaseType()); + for(auto* Prot: OIT->getProtocols()) + VisitObjCProtocolDecl(Prot); + return; + } + if(const TemplateTypeParmType* TTP = T->getAs()) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + if(const TemplateSpecializationType* Spec = T->getAs()) { + Out << '>'; + VisitTemplateName(Spec->getTemplateName()); + Out << Spec->template_arguments().size(); + for(const auto& Arg: Spec->template_arguments()) + VisitTemplateArgument(Arg); + return; + } + if(const DependentNameType* DNT = T->getAs()) { + Out << '^'; + printQualifier(Out, LangOpts, DNT->getQualifier()); + Out << ':' << DNT->getIdentifier()->getName(); + return; + } + if(const InjectedClassNameType* InjT = T->getAs()) { + T = InjT->getInjectedSpecializationType(); + continue; + } + if(const auto* VT = T->getAs()) { + Out << (T->isExtVectorType() ? ']' : '['); + Out << VT->getNumElements(); + T = VT->getElementType(); + continue; + } + if(const auto* const AT = dyn_cast(T)) { + Out << '{'; + switch(AT->getSizeModifier()) { + case ArraySizeModifier::Static: Out << 's'; break; + case ArraySizeModifier::Star: Out << '*'; break; + case ArraySizeModifier::Normal: Out << 'n'; break; + } + if(const auto* const CAT = dyn_cast(T)) + Out << CAT->getSize(); + + T = AT->getElementType(); + continue; + } + + // Unhandled type. + Out << ' '; + break; + } while(true); +} + +void USRGenerator::VisitTemplateParameterList(const TemplateParameterList* Params) { + if(!Params) + return; + Out << '>' << Params->size(); + for(TemplateParameterList::const_iterator P = Params->begin(), PEnd = Params->end(); P != PEnd; + ++P) { + Out << '#'; + if(isa(*P)) { + if(cast(*P)->isParameterPack()) + Out << 'p'; + Out << 'T'; + continue; + } + + if(NonTypeTemplateParmDecl* NTTP = dyn_cast(*P)) { + if(NTTP->isParameterPack()) + Out << 'p'; + Out << 'N'; + VisitType(NTTP->getType()); + continue; + } + + TemplateTemplateParmDecl* TTP = cast(*P); + if(TTP->isParameterPack()) + Out << 'p'; + Out << 't'; + VisitTemplateParameterList(TTP->getTemplateParameters()); + } +} + +void USRGenerator::VisitTemplateName(TemplateName Name) { + if(TemplateDecl* Template = Name.getAsTemplateDecl()) { + if(TemplateTemplateParmDecl* TTP = dyn_cast(Template)) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + + Visit(Template); + return; + } + + // FIXME: Visit dependent template names. +} + +void USRGenerator::VisitTemplateArgument(const TemplateArgument& Arg) { + switch(Arg.getKind()) { + case TemplateArgument::Null: break; + + case TemplateArgument::Declaration: Visit(Arg.getAsDecl()); break; + + case TemplateArgument::NullPtr: break; + + case TemplateArgument::TemplateExpansion: + Out << 'P'; // pack expansion of... + [[fallthrough]]; + case TemplateArgument::Template: + VisitTemplateName(Arg.getAsTemplateOrTemplatePattern()); + break; + + case TemplateArgument::Expression: + // FIXME: Visit expressions. + break; + + case TemplateArgument::Pack: + Out << 'p' << Arg.pack_size(); + for(const auto& P: Arg.pack_elements()) + VisitTemplateArgument(P); + break; + + case TemplateArgument::Type: VisitType(Arg.getAsType()); break; + + case TemplateArgument::Integral: + Out << 'V'; + VisitType(Arg.getIntegralType()); + Out << Arg.getAsIntegral(); + break; + + case TemplateArgument::StructuralValue: { + Out << 'S'; + VisitType(Arg.getStructuralValueType()); + ODRHash Hash{}; + Hash.AddStructuralValue(Arg.getAsStructuralValue()); + Out << Hash.CalculateHash(); + break; + } + } +} + +void USRGenerator::VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl* D) { + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@UUV@"; + printQualifier(Out, LangOpts, D->getQualifier()); + EmitDeclName(D); +} + +void USRGenerator::VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl* D) { + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@UUT@"; + printQualifier(Out, LangOpts, D->getQualifier()); + Out << D->getName(); // Simple name. +} + +void USRGenerator::VisitConceptDecl(const ConceptDecl* D) { + if(ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@CT@"; + EmitDeclName(D); +} + +void USRGenerator::VisitMSGuidDecl(const MSGuidDecl* D) { + VisitDeclContext(D->getDeclContext()); + Out << "@MG@"; + D->NamedDecl::printName(Out); +} + +namespace clice::index { + +bool generateUSRForDecl(const Decl* D, SmallVectorImpl& Buf, const LangOptions& LangOpts) { + if(!D) + return true; + // We don't ignore decls with invalid source locations. Implicit decls, like + // C++'s operator new function, can have invalid locations but it is fine to + // create USRs that can identify them. + + // Check if the declaration has explicit external USR specified. + auto* CD = D->getCanonicalDecl(); + if(auto* ExternalSymAttr = CD->getAttr()) { + if(!ExternalSymAttr->getUSR().empty()) { + llvm::raw_svector_ostream Out(Buf); + Out << ExternalSymAttr->getUSR(); + return false; + } + } + USRGenerator UG(&D->getASTContext(), Buf, LangOpts); + UG.Visit(D); + return UG.ignoreResults(); +} + +bool generateUSRForDecl(const Decl* D, SmallVectorImpl& Buf) { + if(!D) + return true; + return generateUSRForDecl(D, Buf, D->getASTContext().getLangOpts()); +} + +bool generateUSRForMacro(StringRef MacroName, + SourceLocation Loc, + const SourceManager& SM, + SmallVectorImpl& Buf) { + if(MacroName.empty()) + return true; + + llvm::raw_svector_ostream Out(Buf); + + // Assume that system headers are sane. Don't put source location + // information into the USR if the macro comes from a system header. + bool ShouldGenerateLocation = Loc.isValid() && !SM.isInSystemHeader(Loc); + + Out << "c:"; + if(ShouldGenerateLocation) + printLoc(Out, Loc, SM, /*IncludeOffset=*/true); + Out << "@macro@"; + Out << MacroName; + return false; +} } // namespace clice::index + diff --git a/src/Server/Config.cpp b/src/Server/Config.cpp index 003819bc..0de8527c 100644 --- a/src/Server/Config.cpp +++ b/src/Server/Config.cpp @@ -1,9 +1,9 @@ #define TOML_EXCEPTIONS 0 -#include +#include "toml++/toml.hpp" -#include -#include -#include +#include "Server/Config.h" +#include "Support/Logger.h" +#include "llvm/ADT/StringMap.h" namespace clice::config { diff --git a/src/Server/Database.cpp b/src/Server/Database.cpp index 23bdaba2..eda0e58a 100644 --- a/src/Server/Database.cpp +++ b/src/Server/Database.cpp @@ -1,6 +1,6 @@ -#include "Server/Logger.h" +#include "Support/Logger.h" #include "Server/Database.h" -#include "Support/Support.h" +#include "Support/FileSystem.h" #include "Compiler/Compilation.h" namespace clice { diff --git a/src/Server/Indexer.cpp b/src/Server/Indexer.cpp index e200c5d1..df1c053a 100644 --- a/src/Server/Indexer.cpp +++ b/src/Server/Indexer.cpp @@ -3,9 +3,11 @@ #include "Compiler/Compilation.h" #include "Index/SymbolIndex.h" #include "Index/FeatureIndex.h" -#include "Server/Logger.h" +#include "Support/Logger.h" #include "Server/Indexer.h" #include "Support/Assert.h" +#include "Support/Compare.h" + #include "llvm/ADT/StringRef.h" namespace clice { @@ -306,7 +308,7 @@ async::Task<> Indexer::index(this Self& self, llvm::StringRef file) { auto info = co_await async::submit([¶ms] { return compile(params); }); if(!info) { - log::warn("Failed to compile {}: {}", file, info.takeError()); + log::warn("Failed to compile {}: {}", file, info.error()); co_return; } diff --git a/src/Server/Lifecycle.cpp b/src/Server/Lifecycle.cpp index 10482bcd..e32066c4 100644 --- a/src/Server/Lifecycle.cpp +++ b/src/Server/Lifecycle.cpp @@ -1,5 +1,6 @@ #include "Basic/SourceConverter.h" #include "Server/Server.h" +#include "Support/FileSystem.h" namespace clice { diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index d025c08c..44a144bc 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -1,4 +1,4 @@ -#include "Server/Logger.h" +#include "Support/Logger.h" #include "Server/Server.h" namespace clice { @@ -79,7 +79,7 @@ async::Task<> Server::onReceive(json::Value value) { } async::Task<> Server::request(llvm::StringRef method, json::Value params) { - co_await async::write(json::Object{ + co_await async::net::write(json::Object{ {"jsonrpc", "2.0" }, {"id", id += 1 }, {"method", method }, @@ -88,7 +88,7 @@ async::Task<> Server::request(llvm::StringRef method, json::Value params) { } async::Task<> Server::notify(llvm::StringRef method, json::Value params) { - co_await async::write(json::Object{ + co_await async::net::write(json::Object{ {"jsonrpc", "2.0" }, {"method", method }, {"params", std::move(params)}, @@ -96,7 +96,7 @@ async::Task<> Server::notify(llvm::StringRef method, json::Value params) { } async::Task<> Server::response(json::Value id, json::Value result) { - co_await async::write(json::Object{ + co_await async::net::write(json::Object{ {"jsonrpc", "2.0" }, {"id", id }, {"result", std::move(result)}, diff --git a/unittests/Compiler/Resolver.cpp b/unittests/AST/Resolver.cpp similarity index 99% rename from unittests/Compiler/Resolver.cpp rename to unittests/AST/Resolver.cpp index a22cb86f..82262542 100644 --- a/unittests/Compiler/Resolver.cpp +++ b/unittests/AST/Resolver.cpp @@ -1,4 +1,5 @@ #include "Test/CTest.h" +#include "clang/AST/RecursiveASTVisitor.h" namespace clice::testing { diff --git a/unittests/Compiler/SemanticVisitor.cpp b/unittests/AST/SemanticVisitor.cpp similarity index 78% rename from unittests/Compiler/SemanticVisitor.cpp rename to unittests/AST/SemanticVisitor.cpp index c12663df..4d4d139a 100644 --- a/unittests/Compiler/SemanticVisitor.cpp +++ b/unittests/AST/SemanticVisitor.cpp @@ -1,5 +1,5 @@ #include "Test/Test.h" -#include "Compiler/Semantic.h" +#include "AST/Semantic.h" namespace clice::testing { diff --git a/unittests/Compiler/Command.cpp b/unittests/Compiler/Command.cpp index 2cbf0a96..992a349f 100644 --- a/unittests/Compiler/Command.cpp +++ b/unittests/Compiler/Command.cpp @@ -27,10 +27,10 @@ TEST(clice, Command) { " -o CMakeFiles/clice-core.dir/src/Basic/URI.cpp.o" " -c /home/ykiko/C++/clice2/src/Basic/URI.cpp"; - auto error = mangleCommand(command, args, buffer); - ASSERT_FALSE(bool(error)); + auto result = mangleCommand(command, args, buffer); + ASSERT_FALSE(bool(!result)); } } // namespace -} // namespace clice +} // namespace clice::testing diff --git a/unittests/Index/USR.cpp b/unittests/Index/USR.cpp new file mode 100644 index 00000000..39cffc1e --- /dev/null +++ b/unittests/Index/USR.cpp @@ -0,0 +1,37 @@ +#include "Test/CTest.h" +#include "Index/USR.h" +#include "clang/AST/RecursiveASTVisitor.h" + +namespace clice::testing { + +namespace { + +struct ASTVisitor : public clang::RecursiveASTVisitor { + bool VisitDecl(clang::Decl* decl) { + llvm::SmallString<128> buffer; + if(!clice::index::generateUSRForDecl(decl, buffer)) { + llvm::outs() << buffer << "\n"; + } + return true; + } +}; + +TEST(Index, USR) { + const char* content = R"cpp( +int main() { + return 0; +} +)cpp"; + + Tester tester("main.cpp", content); + tester.run(); + + auto& info = *tester.info; + + ASTVisitor visitor; + visitor.TraverseDecl(info.tu()); +} + +} // namespace + +} // namespace clice::testing diff --git a/xmake.lua b/xmake.lua index d9827c27..74b7a9ea 100644 --- a/xmake.lua +++ b/xmake.lua @@ -34,7 +34,6 @@ add_rules("clice_build_config") target("clice-core") set_kind("$(kind)") add_files("src/**.cpp|Driver/*.cpp") - set_pcxxheader("include/Compiler/Clang.h") add_includedirs("include", {public = true}) add_packages("libuv", {public = true})