diff --git a/.vscode/launch.json b/.vscode/launch.json index 70e8de98..30e8dbee 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,9 +18,7 @@ "name": "Launch", "program": "${workspaceFolder}/build/Preamble", "args": [ - "./main.cpp", - "12", - "11" + "/home/ykiko/Project/C++/clice/main.cpp", ], "cwd": "${workspaceFolder}" }, diff --git a/docs/examples/ASTVisitor.cpp b/docs/examples/ASTVisitor.cpp index 3a812441..b8e97365 100644 --- a/docs/examples/ASTVisitor.cpp +++ b/docs/examples/ASTVisitor.cpp @@ -42,18 +42,6 @@ int main(int argc, const char** argv) { std::terminate(); } - if(clang::FileManager* manager = instance->createFileManager()) { - instance->createSourceManager(*manager); - } else { - llvm::errs() << "Failed to create file manager\n"; - std::terminate(); - } - - instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete); - - // ASTContent is necessary for SemanticAnalysis - instance->createASTContext(); - clang::SyntaxOnlyAction action; if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { diff --git a/docs/examples/CodeCompletion.cpp b/docs/examples/CodeCompletion.cpp index 01536620..d33db056 100644 --- a/docs/examples/CodeCompletion.cpp +++ b/docs/examples/CodeCompletion.cpp @@ -1,6 +1,15 @@ #include +#include -// clang 的 CodeCompletion 提供的功能非常有限,无法直接区分当前的 +// clang 的 CodeCompletion 提供的功能非常有限,无法获取当前 Completion 的 scope +// 而获取 scope 是非常重要的一个功能,有了它我们可以做更多事情,比如只把 ...sizeof 显示在可变模板参数的语境下 +// 又或者根据函数名的语境不同(定义和调用)执行不同的补全逻辑,定义的时候就自动补全参数,调用的时候就不补全 +// 我们只能通过一些 hack 的手段来做这个事情了,首先使用 TokenWatcher 可以获取到所有的 Token +// 同样我们可以获取到最终的 tu, + +// TODO: 检查 SemaCodeCompletion.cpp,看看能不能把当前代码补全的 scope 拿到,当然在这之前,先尝试前面那种 hack +// 的 方式,看看能不能同样拿到这些信息,反正 token 位置和最后的语法树都是有的 由于要改 clang +// 源码,所以这不是一个高优先级的任务 ... class CodeCompleteConsumer : public clang::CodeCompleteConsumer { private: @@ -23,6 +32,9 @@ public: auto contexts = Context.getVisitedContexts(); for(auto c: contexts) { llvm::outs() << " Kind: " << c->getDeclKindName() << "\n"; + for(auto d: c->decls()) { + d->dump(); + } } llvm::outs() << "code completion results:\n"; @@ -120,6 +132,9 @@ int main(int argc, const char** argv) { codeCompletionAt.Line = std::stoi(argv[2]); codeCompletionAt.Column = std::stoi(argv[3]); + clang::PreprocessorOptions& popts = invocation->getPreprocessorOpts(); + popts.DetailedRecord = true; + instance->setInvocation(std::move(invocation)); if(!instance->createTarget()) { @@ -127,18 +142,6 @@ int main(int argc, const char** argv) { std::terminate(); } - if(clang::FileManager* manager = instance->createFileManager()) { - instance->createSourceManager(*manager); - } else { - llvm::errs() << "Failed to create file manager\n"; - std::terminate(); - } - - instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete); - - // ASTContent is necessary for SemanticAnalysis - instance->createASTContext(); - /// NOTICE: instance->setCodeCompletionConsumer(new CodeCompleteConsumer()); @@ -151,7 +154,9 @@ int main(int argc, const char** argv) { auto& pp = instance->getPreprocessor(); pp.setTokenWatcher([&pp](const clang::Token& token) { - llvm::outs() << "token: " << pp.getSpelling(token) << " kind: " << token.getName() << "\n"; + if(!token.isAnnotation()) { + // llvm::outs() << "token: " << pp.getSpelling(token) << " kind: " << token.getName() << "\n"; + } }); if(auto error = action.Execute()) { @@ -160,7 +165,7 @@ int main(int argc, const char** argv) { } auto tu = instance->getASTContext().getTranslationUnitDecl(); - tu->dump(); + // tu->dump(); action.EndSourceFile(); } diff --git a/docs/examples/Preamble.cpp b/docs/examples/Preamble.cpp index 2f6d95e9..affd318c 100644 --- a/docs/examples/Preamble.cpp +++ b/docs/examples/Preamble.cpp @@ -1,54 +1,44 @@ -#include #include +#include +#include +#include +#include +#include +#include +#include +#include namespace fs = std::filesystem; -int main(int argc, const char** argv) { - assert(argc == 2 && "Usage: Preamble "); - llvm::outs() << "running Preamble...\n"; - - auto instance = std::make_unique(); - - clang::DiagnosticIDs* ids = new clang::DiagnosticIDs(); - clang::DiagnosticOptions* diag_opts = new clang::DiagnosticOptions(); - clang::DiagnosticConsumer* consumer = new clang::TextDiagnosticPrinter(llvm::errs(), diag_opts); - clang::DiagnosticsEngine* engine = new clang::DiagnosticsEngine(ids, diag_opts, consumer); - instance->setDiagnostics(engine); - - auto invocation = - std::make_shared(std::make_shared()); - std::vector args = { - "/home/ykiko/Project/C++/clice/external/llvm/bin/clang++", - "-Xclang", - "-no-round-trip-args", - "-std=c++20", - "-Wno-everything", - argv[1], - "-c", - }; - - invocation = clang::createInvocation(args, {}); +auto buildPreamble(std::vector args, const char* path) { + auto invocation = clang::createInvocation(args, {}); // from filepath: llvm::MemoryBuffer::getFile // from content: llvm::MemoryBuffer::getMemBuffer(content); - auto tmp = llvm::MemoryBuffer::getFile(argv[1]); + auto tmp = llvm::MemoryBuffer::getFile(path); if(auto error = tmp.getError()) { llvm::errs() << "Failed to get file: " << error.message() << "\n"; std::terminate(); } - llvm::MemoryBuffer* buffer = tmp->get(); + llvm::MemoryBuffer* buffer = tmp->release(); // compute preamble bounds, if MaxLines set to false(0), it means not to limit the number of lines - auto bounds = clang::ComputePreambleBounds({}, *buffer, false); + auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false); - auto VFS = llvm::vfs::getRealFileSystem(); - // if(auto error = VFS->setCurrentWorkingDirectory(fs::path(argv[1]).parent_path().string())) { - // llvm::errs() << "Failed to set current working directory: " << error.message() << "\n"; - // } + // create diagnostic engine + clang::DiagnosticsEngine* engine = new clang::DiagnosticsEngine( + new clang::DiagnosticIDs(), + new clang::DiagnosticOptions(), + new clang::TextDiagnosticPrinter(llvm::errs(), new clang::DiagnosticOptions())); // if store the preamble in memory, if not, store it in a file(storagePath) - bool storeInMemory = true; - std::string storagePath = (fs::path(argv[0]).parent_path()).string(); + bool storeInMemory = false; + std::string storagePath = (fs::path(path).parent_path() / "build").string(); + + auto VFS = llvm::vfs::getRealFileSystem(); + if(auto error = VFS->setCurrentWorkingDirectory(storagePath)) { + llvm::errs() << error.message() << "\n"; + } // use to collect information in the process of building preamble, such as include files and macros // TODO: inherit from clang::PreambleCallbacks and collect the information @@ -59,10 +49,10 @@ int main(int argc, const char** argv) { buffer, bounds, *engine, - VFS, + llvm::vfs::getRealFileSystem(), std::make_shared(), storeInMemory, - "", + storagePath, callbacks); if(auto error = preamble.getError()) { @@ -70,7 +60,39 @@ int main(int argc, const char** argv) { std::terminate(); } + return preamble; +} + +int main(int argc, const char** argv) { + assert(argc == 2 && "Usage: Preamble "); + llvm::outs() << "running Preamble...\n"; + + std::vector args = { + "/home/ykiko/Project/C++/clice/external/llvm/bin/clang++", + "-Xclang", + "-no-round-trip-args", + "-std=c++20", + "-Wno-everything", + argv[1], + }; + + auto preamble = buildPreamble(args, argv[1]); + + auto instance = std::make_unique(); + + auto invocation = std::make_shared(); invocation = clang::createInvocation(args, {}); + + auto tmp = llvm::MemoryBuffer::getFile(argv[1]); + if(auto error = tmp.getError()) { + llvm::errs() << "Failed to get file: " << error.message() << "\n"; + std::terminate(); + } + llvm::MemoryBuffer* buffer = tmp->release(); + + auto VFS = llvm::vfs::getRealFileSystem(); + auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false); + // check if the preamble can be reused if(preamble->CanReuse(*invocation, *buffer, bounds, *VFS)) { llvm::outs() << "Resued preamble\n"; @@ -80,26 +102,19 @@ int main(int argc, const char** argv) { instance->setInvocation(std::move(invocation)); + instance->createDiagnostics( + new clang::TextDiagnosticPrinter(llvm::errs(), new clang::DiagnosticOptions()), + true); + if(!instance->createTarget()) { llvm::errs() << "Failed to create target\n"; std::terminate(); } - if(clang::FileManager* manager = instance->createFileManager()) { - instance->createSourceManager(*manager); - } else { - llvm::errs() << "Failed to create file manager\n"; - std::terminate(); - } - - instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete); - - // ASTContent is necessary for SemanticAnalysis - instance->createASTContext(); - clang::SyntaxOnlyAction action; - if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { + auto& mainInput = instance->getFrontendOpts().Inputs[0]; + if(!action.BeginSourceFile(*instance, mainInput)) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } @@ -109,5 +124,6 @@ int main(int argc, const char** argv) { std::terminate(); } + instance->getASTContext().getTranslationUnitDecl()->dump(); action.EndSourceFile(); } diff --git a/docs/examples/Preprocessor.cpp b/docs/examples/Preprocessor.cpp index 71123806..d22abb00 100644 --- a/docs/examples/Preprocessor.cpp +++ b/docs/examples/Preprocessor.cpp @@ -64,7 +64,9 @@ int main(int argc, const char** argv) { auto invocation = std::make_shared(); std::vector args = { - "/home/ykiko/Project/C++/clice/external/llvm/bin/clang++", + "/usr/local/bin/clang++", + "-x", + "c++", "-Xclang", "-no-round-trip-args", "-std=c++20",