some update.

This commit is contained in:
ykiko
2024-09-21 11:48:55 +08:00
parent ebddecede8
commit d030bc2800
17 changed files with 296 additions and 71 deletions

6
.vscode/launch.json vendored
View File

@@ -8,18 +8,18 @@
"type": "lldb",
"request": "launch",
"name": "clice_socket",
"program": "./build/bin/clice"
"program": "${workspaceFolder}/build/bin/clice"
},
{
"type": "lldb",
"request": "attach",
"name": "clice",
"name": "clice_attach",
"program": "clice"
},
{
"type": "lldb",
"request": "launch",
"name": "DependentNameResolver",
"name": "clice_test",
"program": "${workspaceFolder}/build/bin/clice_test",
"cwd": "${workspaceFolder}"
}

16
docs/design.md Normal file
View File

@@ -0,0 +1,16 @@
clice 充分吸取了 clangd 教训,用一些全新的设计来解决问题。
- 默认索引所有文件
- 对于只读文件,使用 LSIF 返回请求结果speed speed speed !!!!
- 对于可变文件,为它们构建 preamble以加速后续的构建然后跑代码补全之类的行为
- clice 如何区分只读文件和可变文件一般来说当您第一次尝试编辑这个文件的时候clice 就会认为这是一个可变的文件,并为其构建 preamble。但是很多情况下可能只是不小心碰到了键盘并不是真的想要修改可以在设置里面配置选项使得第一次更改并报错之后才会让 clice 认为这是一个活跃文件。另外,使用 code action 也会触发。
# non self contained
clice 解决了一个 clangd 里面长期存在的问题non self contained 的头文件,这种技巧常见于 glibc 或者 libstdc++ 这样的代码库中。non self-contained file 指的是那些不能单独编译的源文件。只有把他们一同拼接到一个文件中,才能编译通过,而且对于这种文件,往往 include 顺序也十分重要,建议关闭头文件顺序重排。
对于 non self contained 的头文件来说,只要生成的编译数据库中,有包含它的源文件,并且能正常编译,那么 clice 就可以正确为它生成索引,从而支持转到定义等等等操作。
那么 non self contained 的文件对于可变操作如何呢TODO: ... 初步设想,手动通过字符串拼接的方式来补全缺少的头文件 ...

View File

@@ -20,3 +20,5 @@
- Go to implementation
常用于跳转到虚函数实现,部分情况下。特别的,如果 clice 能发现你这个类型是一个 CRTP 类型,那么它也可以进行对应的跳转。
#

19
include/AST/Compiler.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <Support/ADT.h>
#include <clang/Frontend/CompilerInstance.h>
namespace clice {
// TODO:
class Preamble;
std::unique_ptr<clang::CompilerInvocation> createInvocation(StringRef filename,
StringRef content,
std::vector<const char*>& args,
Preamble* preamble = nullptr);
std::unique_ptr<clang::CompilerInstance> createInstance(std::shared_ptr<clang::CompilerInvocation> invocation);
} // namespace clice

View File

@@ -36,6 +36,7 @@ struct Directive {
};
struct Directives {
clang::Preprocessor& preproc;
clang::SourceManager& sourceManager;
llvm::DenseMap<clang::FileID, Directive> x;

View File

@@ -13,8 +13,9 @@ namespace clice {
struct Preamble {
clang::PrecompiledPreamble data;
static std::unique_ptr<Preamble>
build(llvm::StringRef filename, llvm::StringRef content, std::vector<const char*>& args);
static std::unique_ptr<Preamble> build(llvm::StringRef filename,
llvm::StringRef content,
std::vector<const char*>& args);
};
} // namespace clice

View File

@@ -1,9 +1,31 @@
#include "ParsedAST.h"
#include <clang/AST/ASTTypeTraits.h>
namespace clice {
class Selection {};
class SelectionTree {
public:
enum Selection {
Unselected,
Partial,
Complete,
};
clang::Decl* test(clang::syntax::Token* token, ParsedAST& AST, llvm::StringRef filename);
struct Node {
Node* parent;
llvm::SmallVector<const Node*> children;
clang::DynTypedNode ASTNode;
};
const Node* commonAncestor() const;
const Node& root() const;
SelectionTree(clang::ASTContext& context, const clang::syntax::TokenBuffer& tokens, unsigned start, unsigned end);
private:
std::deque<Node> m_Nodes;
const Node* m_Root;
};
} // namespace clice

22
include/Index/Index.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <clang/Index/IndexDataConsumer.h>
namespace clice {
class IndexConsumer : public clang::index::IndexDataConsumer {
public:
void initialize(clang::ASTContext& Ctx) override {}
void setPreprocessor(std::shared_ptr<clang::Preprocessor> PP) override {}
bool handleDeclOccurrence(const clang::Decl* D,
clang::index::SymbolRoleSet Roles,
llvm::ArrayRef<clang::index::SymbolRelation> Relations,
clang::SourceLocation Loc,
clang::index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
return true;
}
};
} // namespace clice

52
include/Index/Protocol.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <Support/ADT.h>
namespace clice {
struct SymbolID {};
struct Range {};
enum class SymbolKind : uint8_t {
};
struct Relation {
SymbolID target;
bool isReference = false;
bool isImplementation = false;
bool isTypeDefinition = false;
bool isBase = false;
bool isOverride = false;
};
struct Symbol {
SymbolID symbol;
SymbolKind kind;
StringRef displayName;
StringRef document;
std::vector<Relation> relations;
};
enum class SymbolRole : uint8_t {
Definition,
Import,
WriteAccess,
ReadAccess,
ForwardDeclaration,
};
struct Occurrence {
SymbolID symbol;
Range range;
SymbolRole role;
};
struct Document {
StringRef uri;
std::vector<Symbol> symbols;
std::vector<Occurrence> occurrences;
};
}; // namespace clice

17
include/Support/ADT.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/SmallString.h>
namespace clice {
using llvm::ArrayRef;
using llvm::StringRef;
using llvm::SmallVector;
using llvm::SmallString;
} // namespace clice

56
src/AST/Compiler.cpp Normal file
View File

@@ -0,0 +1,56 @@
#include <AST/Compiler.h>
#include <AST/Preamble.h>
#include <clang/Lex/PreprocessorOptions.h>
namespace clice {
static void setInvocation(clang::CompilerInvocation& invocation) {
clang::LangOptions& langOpts = invocation.getLangOpts();
langOpts.CommentOpts.ParseAllComments = true;
langOpts.RetainCommentsFromSystemHeaders = true;
}
std::unique_ptr<clang::CompilerInvocation>
createInvocation(StringRef filename, StringRef content, std::vector<const char*>& args, Preamble* preamble) {
clang::CreateInvocationOptions options;
// FIXME: explore VFS
auto vfs = llvm::vfs::getRealFileSystem();
auto invocation = clang::createInvocation(args, options);
setInvocation(*invocation);
auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content, filename);
if(preamble) {
auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false);
// check if the preamble can be reused
if(preamble->data.CanReuse(*invocation, *buffer, bounds, *vfs)) {
llvm::outs() << "Resued preamble\n";
// reuse preamble
preamble->data.AddImplicitPreamble(*invocation, vfs, buffer.release());
}
} else {
invocation->getPreprocessorOpts().addRemappedFile(invocation->getFrontendOpts().Inputs[0].getFile(),
buffer.release());
}
return invocation;
}
std::unique_ptr<clang::CompilerInstance> createInstance(std::shared_ptr<clang::CompilerInvocation> invocation) {
auto instance = std::make_unique<clang::CompilerInstance>();
instance->setInvocation(std::move(invocation));
// FIXME: resolve diagnostics
instance->createDiagnostics(new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true);
if(!instance->createTarget()) {
llvm::errs() << "Failed to create target\n";
std::terminate();
}
return instance;
}
} // namespace clice

View File

@@ -1,5 +1,6 @@
#include <AST/Directive.h>
#include <Support/Reflection.h>
#include <clang/Lex/MacroArgs.h>
namespace clice {
@@ -12,7 +13,7 @@ struct CommentHandler : public clang::CommentHandler {
// directive.comments.push_back(Comment);
auto start = directive.sourceManager.getCharacterData(Comment.getBegin());
auto end = directive.sourceManager.getCharacterData(Comment.getEnd());
llvm::outs() << "Comment: " << llvm::StringRef(start, end - start) << "\n";
// llvm::outs() << "Comment: " << llvm::StringRef(start, end - start) << "\n";
return false;
}
};
@@ -58,7 +59,7 @@ struct PPCallback : clang::PPCallbacks {
void If(clang::SourceLocation Loc,
clang::SourceRange ConditionRange,
clang::PPCallbacks::ConditionValueKind ConditionValue) override {
llvm::outs() << "If\n";
// llvm::outs() << "If\n";
}
void Elif(clang::SourceLocation loc,
@@ -92,6 +93,37 @@ struct PPCallback : clang::PPCallbacks {
void Endif(clang::SourceLocation loc, clang::SourceLocation ifLoc) override {}
void MacroDefined(const clang::Token& MacroNameTok, const clang::MacroDirective* MD) override {}
void MacroExpands(const clang::Token& MacroNameTok,
const clang::MacroDefinition& MD,
clang::SourceRange Range,
const clang::MacroArgs* Args) override {
// llvm::outs() << "------------------------\n";
// auto info = MD.getMacroInfo();
// if(info->isObjectLike()) {
// Range = clang::SourceRange(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
// } else if(info->isFunctionLike()) {
// // MacroNameTok.getLocation().dump(directive.sourceManager);
// }
// Range.getBegin().dump(directive.sourceManager);
// Range.getEnd().dump(directive.sourceManager);
// auto s = directive.sourceManager.getCharacterData(Range.getBegin());
// auto e = directive.sourceManager.getCharacterData(Range.getEnd());
//// llvm::outs() << llvm::StringRef(s, e - s) << "\n";
//
// llvm::outs() << directive.preproc.getSpelling(MacroNameTok) << " ";
// if(Args) {
// auto size = Args->getNumMacroArguments();
// for(int i = 0; i < size; i++) {
// auto arg = Args->getUnexpArgument(i);
// auto len = Args->getArgLength(arg);
// for(int j = 0; j < len; j++) {
// llvm::outs() << directive.preproc.getSpelling(arg[j]) << " ";
// }
// }
//}
// llvm::outs() << "\n";
}
};
clang::CommentHandler* Directives::handler() { return new CommentHandler(*this); }

View File

@@ -1,14 +1,8 @@
#include <AST/ParsedAST.h>
#include "clang/Lex/PreprocessorOptions.h"
#include <AST/Compiler.h>
namespace clice {
static void setInvocation(clang::CompilerInvocation& invocation) {
clang::LangOptions& langOpts = invocation.getLangOpts();
langOpts.CommentOpts.ParseAllComments = true;
langOpts.RetainCommentsFromSystemHeaders = true;
}
std::unique_ptr<ParsedAST> ParsedAST::build(llvm::StringRef filename,
llvm::StringRef content,
std::vector<const char*>& args,
@@ -18,43 +12,8 @@ std::unique_ptr<ParsedAST> ParsedAST::build(llvm::StringRef filename,
clang::CreateInvocationOptions options;
// options.VFS = vfs;
auto invocation = std::make_shared<clang::CompilerInvocation>();
invocation = clang::createInvocation(args, options);
auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content, filename);
if(preamble) {
auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false);
// check if the preamble can be reused
if(preamble->data.CanReuse(*invocation, *buffer, bounds, *vfs)) {
llvm::outs() << "Resued preamble\n";
// reuse preamble
preamble->data.AddImplicitPreamble(*invocation, vfs, buffer.release());
}
} else {
invocation->getPreprocessorOpts().addRemappedFile(invocation->getFrontendOpts().Inputs[0].getFile(),
buffer.release());
}
invocation->getLangOpts().CommentOpts.ParseAllComments = true;
auto instance = std::make_unique<clang::CompilerInstance>();
instance->setInvocation(std::move(invocation));
instance->createDiagnostics(new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true);
// if(auto remappingVSF =
// createVFSFromCompilerInvocation(instance->getInvocation(), instance->getDiagnostics(), vfs)) {
// vfs = remappingVSF;
// }
// instance->createFileManager(vfs);
if(!instance->createTarget()) {
llvm::errs() << "Failed to create target\n";
std::terminate();
}
auto invocation = createInvocation(filename, content, args, preamble);
auto instance = createInstance(std::move(invocation));
auto action = std::make_unique<clang::SyntaxOnlyAction>();
if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {
@@ -65,7 +24,7 @@ std::unique_ptr<ParsedAST> ParsedAST::build(llvm::StringRef filename,
auto& preproc = instance->getPreprocessor();
clang::syntax::TokenCollector collector(preproc);
auto directive = std::make_unique<Directives>(instance->getSourceManager());
auto directive = std::make_unique<Directives>(instance->getPreprocessor(), instance->getSourceManager());
preproc.addCommentHandler(directive->handler());
preproc.addPPCallbacks(directive->callback());

View File

@@ -2,10 +2,26 @@
namespace clice {
std::unique_ptr<Preamble>
Preamble::build(llvm::StringRef filename, llvm::StringRef content, std::vector<const char*>& args) {
class PreambleCallback : public clang::PreambleCallbacks {
public:
std::optional<clang::syntax::TokenCollector> collector;
public:
void BeforeExecute(clang::CompilerInstance& CI) override { collector.emplace(CI.getPreprocessor()); }
void AfterExecute(clang::CompilerInstance& CI) override {
auto tokens = std::move(collector.value()).consume();
for(auto& token: tokens.expandedTokens()) {
llvm::outs() << token.text(CI.getSourceManager()) << "\n";
}
}
};
std::unique_ptr<Preamble> Preamble::build(llvm::StringRef filename,
llvm::StringRef content,
std::vector<const char*>& args) {
auto invocation = clang::createInvocation(args, {});
auto buffer = llvm::MemoryBuffer::getMemBuffer(content, filename);
auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content, filename);
// compute preamble bounds, i.e. the range of the preamble.
// if MaxLines set to false(0), i.e. limit the number of lines.
@@ -19,7 +35,7 @@ std::unique_ptr<Preamble>
// 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
clang::PreambleCallbacks callbacks = {};
PreambleCallback callbacks = {};
auto preamble = clang::PrecompiledPreamble::Build(*invocation,
buffer.get(),

View File

@@ -23,14 +23,21 @@ TEST(test, test) {
#include <cstdio>
const char* code = R"(
// 123
#if x
#endif
// 333
void f();
void f() {}
)";
auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs);
auto preamble = clice::Preamble::build("main.cpp", code, compileArgs);
auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs, preamble.get());
auto fileID = AST->getFileID("main.cpp");
auto tokens = AST->tokenBuffer.spelledTokens(fileID);
AST->context.getTranslationUnitDecl()->dump();
// for(auto& token: tokens) {
// llvm::outs() << token.text(AST->sourceManager) << "\n";
// }
// AST->context.getTranslationUnitDecl()->dump();
// auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp");
}

View File

@@ -0,0 +1,3 @@
#include <gtest/gtest.h>
TEST(clang, TokenBuffer) {}

View File

@@ -6,25 +6,25 @@ namespace {
using namespace clice;
TEST(URITest, ConstructorAndAccessors) {
URI uri("https", "reviews.lvm.org", "/D41946");
URI uri("https", "reviews.llvm.org", "/D41946");
EXPECT_EQ(uri.scheme(), "https");
EXPECT_EQ(uri.authority(), "reviews.lvm.org");
EXPECT_EQ(uri.authority(), "reviews.llvm.org");
EXPECT_EQ(uri.body(), "/D41946");
}
TEST(URITest, CopyConstructor) {
URI uri1("https", "reviews.lvm.org", "/D41946");
URI uri1("https", "reviews.llvm.org", "/D41946");
URI uri2(uri1);
EXPECT_EQ(uri2.scheme(), "https");
EXPECT_EQ(uri2.authority(), "reviews.lvm.org");
EXPECT_EQ(uri2.authority(), "reviews.llvm.org");
EXPECT_EQ(uri2.body(), "/D41946");
}
TEST(URITest, EqualityOperator) {
URI uri1("https", "reviews.lvm.org", "/D41946");
URI uri2("https", "reviews.lvm.org", "/D41946");
URI uri1("https", "reviews.llvm.org", "/D41946");
URI uri2("https", "reviews.llvm.org", "/D41946");
URI uri3("http", "example.com", "/index.html");
EXPECT_TRUE(uri1 == uri2);
@@ -32,12 +32,12 @@ TEST(URITest, EqualityOperator) {
}
TEST(URITest, ParseFunction) {
auto expectedUri = URI::parse("https://reviews.lvm.org/D41946");
auto expectedUri = URI::parse("https://reviews.llvm.org/D41946");
ASSERT_TRUE(static_cast<bool>(expectedUri));
URI uri = expectedUri.get();
EXPECT_EQ(uri.scheme(), "https");
EXPECT_EQ(uri.authority(), "reviews.lvm.org");
EXPECT_EQ(uri.authority(), "reviews.llvm.org");
EXPECT_EQ(uri.body(), "/D41946");
}