diff --git a/include/AST/Semantic.h b/include/AST/Semantic.h index 782461c5..6bb763a3 100644 --- a/include/AST/Semantic.h +++ b/include/AST/Semantic.h @@ -102,7 +102,13 @@ public: } void run() { - Base::TraverseAST(unit.context()); + if(Base::interestedOnly) { + for(auto decl: unit.top_level_decls()) { + Base::TraverseDecl(decl); + } + } else { + Base::TraverseAST(unit.context()); + } for(auto directive: unit.directives()) { for(auto macro: directive.second.macros) { diff --git a/include/AST/SourceCode.h b/include/AST/SourceCode.h index 81915db4..2a8de6f1 100644 --- a/include/AST/SourceCode.h +++ b/include/AST/SourceCode.h @@ -28,29 +28,103 @@ struct LocalSourceRange { } }; -/// Get the content of the file with the given file ID. -llvm::StringRef getFileContent(const clang::SourceManager& SM, clang::FileID fid); +struct Token { + /// Whether this token is at the start of line. + bool is_at_start_of_line = false; -/// Get the length of the token at the given location. All SourceLocation instances in the clang -/// AST originate from the start position of tokens, which helps reduce memory usage. When token -/// length information is needed, a simple lexing operation based on the start position can be -/// performed. -std::uint32_t getTokenLength(const clang::SourceManager& SM, clang::SourceLocation location); + /// Whether this token is a preprocessor directive. + bool is_preprocessor_directive = false; -/// Get the spelling of the token at the given location. -llvm::StringRef getTokenSpelling(const clang::SourceManager& SM, clang::SourceLocation location); + /// The kind of this token. + clang::tok::TokenKind kind; -/// A fake location could be used to calculate the token location offset when lexer -/// runs in raw mode. -inline clang::SourceLocation fakeLoc = clang::SourceLocation::getFromRawEncoding(1); + /// The source range of this token. + LocalSourceRange range; -/// @brief Run `clang::Lexer` in raw mode and tokenize the content. -/// @param content The content to tokenize. -/// @param callback The callback to call for each token. Return false to break. -/// @param langOpts The language options to use. If not provided, lastest C++ standard is used. -void tokenize(llvm::StringRef content, - llvm::unique_function callback, - bool ignoreComments = true, - const clang::LangOptions* langOpts = nullptr); + bool valid() { + return range.valid(); + } + + llvm::StringRef name() { + return clang::tok::getTokenName(kind); + } + + llvm::StringRef text(llvm::StringRef content) { + assert(range.valid() && "Invalid source range"); + return content.substr(range.begin, range.end - range.begin); + } + + bool is_eof() { + return kind == clang::tok::eof; + } + + bool is_identifier() { + return kind == clang::tok::raw_identifier; + } + + bool is_directive_hash() { + return is_at_start_of_line && kind == clang::tok::hash; + } + + /// The tokens after the include diretive are regarded as + /// a whole token, whose kind is `header_name`. For example + /// `` and `"test.h"` are both header name. + bool is_header_name() { + return kind == clang::tok::header_name; + } +}; + +class Lexer { +public: + Lexer(llvm::StringRef content, + bool ignore_comments = true, + const clang::LangOptions* lang_opts = nullptr, + bool ignore_end_of_directive = true); + + Lexer(const Lexer&) = delete; + + Lexer(Lexer&&) = delete; + + ~Lexer(); + + void lex(Token& token); + + /// Get the token before this token without moving the lexer. + Token last(); + + /// Get the token after this token without moving the lexer. + Token next(); + + /// Advance the lexer and return the next token. + Token advance(); + + /// Advance the lexer until meet the specific kind token. + Token advance_until(clang::tok::TokenKind kind); + +private: + /// If this is set to false, the lexer will emit tok::eod at the end + /// of directive. + bool ignore_end_of_directive = true; + + /// Whether we are lexing the preprocessor directive. + bool parse_preprocessor_directive = false; + + /// Whether we are lexing the header name. + bool parse_header_name = false; + + /// The cache of last token. + Token last_token; + + /// The cache of current token. + Token current_token; + + /// The cache of next token. + std::optional next_token; + + /// The lexed content. + llvm::StringRef content; + + void* impl; +}; } // namespace clice diff --git a/include/Compiler/Compilation.h b/include/Compiler/Compilation.h index 42aeb390..a6ed7789 100644 --- a/include/Compiler/Compilation.h +++ b/include/Compiler/Compilation.h @@ -13,7 +13,7 @@ namespace clice { struct CompilationParams { /// Output file path. - llvm::SmallString<128> outPath; + llvm::SmallString<128> output_file; std::string directory; @@ -36,7 +36,7 @@ struct CompilationParams { /// A flag to inform to stop compilation, this is very useful /// to cancel old compilation task. - std::shared_ptr stop; + std::shared_ptr stop = std::make_shared(false); /// Store all compilation errors in the process. std::shared_ptr> diagnostics; diff --git a/include/Compiler/CompilationUnit.h b/include/Compiler/CompilationUnit.h index 50b22204..7eab271e 100644 --- a/include/Compiler/CompilationUnit.h +++ b/include/Compiler/CompilationUnit.h @@ -122,6 +122,9 @@ public: /// Get the expanded tokens(after preprocessing) of the file id. auto expanded_tokens(clang::SourceRange range) -> llvm::ArrayRef; + /// Get the token length. + auto token_length(clang::SourceLocation location) -> std::uint32_t; + /// Get the spelling of the token corresponding to the location. auto token_spelling(clang::SourceLocation location) -> llvm::StringRef; @@ -134,6 +137,8 @@ public: /// Return all diagnostics in the process of compilation. auto diagnostics() -> llvm::ArrayRef; + auto top_level_decls() -> llvm::ArrayRef; + clang::LangOptions& lang_options(); clang::ASTContext& context(); diff --git a/include/Compiler/Directive.h b/include/Compiler/Directive.h index 6cc7335e..84b67253 100644 --- a/include/Compiler/Directive.h +++ b/include/Compiler/Directive.h @@ -19,7 +19,7 @@ struct Include { clang::SourceLocation location; /// The range of filename(includes `""` or `<>`). - clang::SourceRange fileNameRange; + clang::SourceRange filename_range; }; /// Information about `__has_include` directive. @@ -67,7 +67,7 @@ struct Condition { clang::SourceLocation loc; /// Range of the condition. - clang::SourceRange conditionRange; + clang::SourceRange condition_range; }; /// Information about macro definition, reference and undef. @@ -129,7 +129,7 @@ struct Import { struct Directive { std::vector includes; - std::vector hasIncludes; + std::vector has_includes; std::vector conditions; std::vector macros; std::vector pragmas; diff --git a/include/Compiler/Preamble.h b/include/Compiler/Preamble.h index eb5cb39d..7ecdccc3 100644 --- a/include/Compiler/Preamble.h +++ b/include/Compiler/Preamble.h @@ -31,10 +31,10 @@ struct PCHInfo { /// Compute the preamble bound of given content. We just /// run lex until we find first not directive. -std::uint32_t computePreambleBound(llvm::StringRef content); +std::uint32_t compute_preamble_bound(llvm::StringRef content); /// Same as above, but return a group of bounds for chained PCH /// building. -std::vector computePreambleBounds(llvm::StringRef content); +std::vector compute_preamble_bounds(llvm::StringRef content); } // namespace clice diff --git a/include/Feature/SemanticToken.h b/include/Feature/SemanticToken.h index d15b2ae2..976ba5de 100644 --- a/include/Feature/SemanticToken.h +++ b/include/Feature/SemanticToken.h @@ -21,7 +21,7 @@ struct SemanticToken { using SemanticTokens = std::vector; /// Generate semantic tokens for the interested file only. -SemanticTokens semanticTokens(CompilationUnit& unit); +SemanticTokens semantic_tokens(CompilationUnit& unit); /// Generate semantic tokens for all files. index::Shared indexSemanticToken(CompilationUnit& unit); diff --git a/include/Test/Annotation.h b/include/Test/Annotation.h index e8155bb4..8ba3863a 100644 --- a/include/Test/Annotation.h +++ b/include/Test/Annotation.h @@ -5,49 +5,99 @@ namespace clice::testing { -class Annotation { -public: - Annotation(llvm::StringRef content) { - std::uint32_t offset = 0; +struct AnnotatedSource { + std::string content; + llvm::StringMap offsets; + static AnnotatedSource from(llvm::StringRef content) { + std::string source; + llvm::StringMap offsets; + + source.reserve(content.size()); + + std::uint32_t offset = 0; for(uint32_t i = 0; i < content.size();) { auto c = content[i]; if(c == '@') { - /// match @name i += 1; auto key = content.substr(i).take_until([](char c) { return c == ' '; }); - assert(!offsets.contains(key) && "duplicate key"); - offsets.try_emplace(key, offset); - continue; - } else if(c == '$') { - /// match $(name) - assert(i + 1 < content.size() && content[i + 1] == '(' && "expect $(name)"); - i += 2; - auto key = content.substr(i).take_until([](char c) { return c == ')'; }); - i += key.size() + 1; - assert(!offsets.contains(key) && "duplicate key"); offsets.try_emplace(key, offset); continue; } - offset += 1; + if(c == '$') { + assert(i + 1 < content.size() && content[i + 1] == '(' && "expect $(name)"); + i += 2; + auto key = content.substr(i).take_until([](char c) { return c == ')'; }); + i += key.size() + 1; + offsets.try_emplace(key, offset); + continue; + } + i += 1; - m_source.push_back(c); + offset += 1; + source += c; } + + return AnnotatedSource{std::move(source), std::move(offsets)}; + } +}; + +struct AnnotatedSources { + /// All sources file in the compilation. + llvm::StringMap all_files; + + void add_source(llvm::StringRef file, llvm::StringRef content) { + all_files.try_emplace(file, AnnotatedSource::from(content)); } - std::uint32_t offset(llvm::StringRef name) { - return offsets[name]; - } + /// Add sources to the params, use `#[filename]` to mark + /// a new file start. For example + /// + /// ```cpp + /// #[test.h] + /// int foo(); + /// + /// #[main.cpp] + /// #include "test.h" + /// int x = foo(); + /// ``` + void add_sources(llvm::StringRef content) { + std::string curr_file; + std::string curr_content; - llvm::StringRef source() { - return m_source; - } + /// Save previous file to params. + auto save_previous_file = [&]() { + if(curr_file.empty()) { + return; + } -private: - std::string m_source; - llvm::StringMap offsets; + add_source(curr_file, curr_content); + curr_file.clear(); + curr_content.clear(); + }; + + while(!content.empty()) { + llvm::StringRef line = content.take_front(content.find_first_of("\r\n")); + content = content.drop_front(line.size()); + if(content.starts_with("\n")) { + content = content.drop_front(2); + } else if(content.starts_with("\n")) { + content = content.drop_front(1); + } + + if(line.starts_with("#[") && line.ends_with("]")) { + save_previous_file(); + curr_file = line.slice(2, line.size() - 1).str(); + } else if(!curr_file.empty()) { + curr_content += line; + curr_content += '\n'; + } + } + + save_previous_file(); + } }; } // namespace clice::testing diff --git a/include/Test/CTest.h b/include/Test/CTest.h deleted file mode 100644 index 395af605..00000000 --- a/include/Test/CTest.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "Test.h" -#include "Annotation.h" -#include "Protocol/Protocol.h" -#include "Compiler/Command.h" -#include "Compiler/Compilation.h" - -namespace clice::testing { - -struct Tester { - CompilationParams params; - CompilationDatabase database; - std::optional unit; - std::string src_path; - - /// Annoated locations. - llvm::StringMap offsets; - std::vector sources; - -public: - Tester() = default; - - Tester(llvm::StringRef file, llvm::StringRef content) { - src_path = file; - params.add_remapped_file(file, annoate(content)); - } - - void addMain(llvm::StringRef file, llvm::StringRef content) { - src_path = file; - params.add_remapped_file(file, annoate(content)); - } - - void addFile(llvm::StringRef name, llvm::StringRef content) { - params.add_remapped_file(name, annoate(content)); - } - - llvm::StringRef annoate(llvm::StringRef content) { - auto& source = sources.emplace_back(); - source.reserve(content.size()); - - uint32_t line = 0; - uint32_t column = 0; - uint32_t offset = 0; - - for(uint32_t i = 0; i < content.size();) { - auto c = content[i]; - - if(c == '@') { - i += 1; - auto key = content.substr(i).take_until([](char c) { return c == ' '; }); - offsets.try_emplace(key, offset); - continue; - } - - if(c == '$') { - assert(i + 1 < content.size() && content[i + 1] == '(' && "expect $(name)"); - i += 2; - auto key = content.substr(i).take_until([](char c) { return c == ')'; }); - i += key.size() + 1; - offsets.try_emplace(key, offset); - continue; - } - - if(c == '\n') { - line += 1; - column = 0; - } else { - column += 1; - } - - i += 1; - offset += 1; - - source.push_back(c); - } - - return source; - } - - Tester& compile(llvm::StringRef standard = "-std=c++20") { - auto command = std::format("clang++ {} {} -fms-extensions", standard, src_path); - - database.update_command("fake", src_path, command); - params.arguments = database.get_command(src_path).arguments; - - auto info = clice::compile(params); - ASSERT_TRUE(info); - this->unit.emplace(std::move(*info)); - return *this; - } - - std::uint32_t offset(llvm::StringRef key) const { - return offsets.lookup(key); - } -}; - -struct TestFixture : ::testing::Test, Tester {}; - -} // namespace clice::testing - diff --git a/include/Test/IndexTester.h b/include/Test/IndexTester.h deleted file mode 100644 index 1a9ef89b..00000000 --- a/include/Test/IndexTester.h +++ /dev/null @@ -1,40 +0,0 @@ -#include "Test/CTest.h" -#include "Index/SymbolIndex.h" - -namespace clice::testing { - -struct IndexTester : Tester { - std::vector binary; - llvm::DenseMap> indices; - using Tester::Tester; - - IndexTester& run(const char* standard = "-std=c++20") { - compile(standard); - /// indices = index::index(*info); - return *this; - } - - IndexTester& GotoDefinition(llvm::StringRef cursor, - llvm::StringRef target, - LocationChain chain = LocationChain()) { - /// SourceConverter converter; - /// auto offset = converter.toOffset(sources[0], pos(cursor)); - - /// llvm::SmallVector symbols; - /// indices.begin()->second.locateSymbols(offset, symbols); - /// EXPECT_EQ(symbols.size(), 1, current); - - /// bool found = false; - /// for(auto&& relation: symbols[0].relations()) { - /// if(relation.kind() & RelationKind::Definition) { - /// EXPECT_EQ(relation.range()->begin, offsets[target], current); - /// found = true; - /// } - /// } - /// EXPECT_TRUE(found); - - return *this; - } -}; - -} // namespace clice::testing diff --git a/include/Test/Test.h b/include/Test/Test.h index 143fcfa8..3968ec4e 100644 --- a/include/Test/Test.h +++ b/include/Test/Test.h @@ -5,7 +5,6 @@ #include "Support/Format.h" #include "Support/Compare.h" #include "Support/FileSystem.h" -#include "Annotation.h" #include "Test/LocationChain.h" namespace clice::testing { diff --git a/include/Test/Tester.h b/include/Test/Tester.h new file mode 100644 index 00000000..f854573d --- /dev/null +++ b/include/Test/Tester.h @@ -0,0 +1,101 @@ +#pragma once + +#include "Test.h" +#include "Annotation.h" +#include "Protocol/Protocol.h" +#include "Compiler/Command.h" +#include "Compiler/Compilation.h" + +namespace clice::testing { + +struct Tester { + CompilationParams params; + CompilationDatabase database; + std::optional unit; + std::string src_path; + + /// All sources file in the compilation. + AnnotatedSources sources; + + void add_main(llvm::StringRef file, llvm::StringRef content) { + src_path = file; + sources.add_source(file, content); + } + + void add_file(llvm::StringRef name, llvm::StringRef content) { + sources.add_source(name, content); + } + + Tester& compile(llvm::StringRef standard = "-std=c++20") { + auto command = std::format("clang++ {} {} -fms-extensions", standard, src_path); + + database.update_command("fake", src_path, command); + params.arguments = database.get_command(src_path).arguments; + + for(auto& [file, source]: sources.all_files) { + params.add_remapped_file(file, source.content); + } + + auto info = clice::compile(params); + ASSERT_TRUE(info); + this->unit.emplace(std::move(*info)); + return *this; + } + + bool compile_with_pch(llvm::StringRef standard = "-std=c++20") { + auto command = std::format("clang++ {} {} -fms-extensions", standard, src_path); + + database.update_command("fake", src_path, command); + params.arguments = database.get_command(src_path, true, true).arguments; + + auto path = fs::createTemporaryFile("clice", "pch"); + if(!path) { + llvm::outs() << path.error().message() << "\n"; + } + + /// Build PCH + params.output_file = *path; + + for(auto& [file, source]: sources.all_files) { + if(file == src_path) { + auto bound = compute_preamble_bound(source.content); + params.add_remapped_file(file, source.content.substr(0, bound)); + } else { + params.add_remapped_file(file, source.content); + } + } + + PCHInfo info; + { + auto unit = clice::compile(params, info); + if(!unit) { + llvm::outs() << unit.error() << "\n"; + return false; + } + } + + /// Build AST + params.output_file.clear(); + params.pch = {info.path, info.preamble.size()}; + for(auto& [file, source]: sources.all_files) { + params.add_remapped_file(file, source.content); + } + + auto unit = clice::compile(params); + if(!unit) { + return false; + } + + this->unit.emplace(std::move(*unit)); + return true; + } + + std::uint32_t operator[] (llvm::StringRef file, llvm::StringRef pos) { + return sources.all_files.lookup(file).offsets.lookup(pos); + } +}; + +struct TestFixture : ::testing::Test, Tester {}; + +} // namespace clice::testing + diff --git a/src/AST/SourceCode.cpp b/src/AST/SourceCode.cpp index b61b54a5..b7ba5b46 100644 --- a/src/AST/SourceCode.cpp +++ b/src/AST/SourceCode.cpp @@ -5,43 +5,101 @@ namespace clice { -llvm::StringRef getFileContent(const clang::SourceManager& SM, clang::FileID fid) { - return SM.getBufferData(fid); -} - std::uint32_t getTokenLength(const clang::SourceManager& SM, clang::SourceLocation location) { return clang::Lexer::MeasureTokenLength(location, SM, {}); } -llvm::StringRef getTokenSpelling(const clang::SourceManager& SM, clang::SourceLocation location) { - return llvm::StringRef(SM.getCharacterData(location), getTokenLength(SM, location)); +/// A fake location could be used to calculate the token location offset when lexer +/// runs in raw mode. +inline static clang::SourceLocation fake_loc = clang::SourceLocation::getFromRawEncoding(1); + +Lexer::Lexer(llvm::StringRef content, + bool ignore_comments, + const clang::LangOptions* lang_opts, + bool ignore_end_of_directive) : + content(content), ignore_end_of_directive(ignore_end_of_directive) { + + static clang::LangOptions default_opts; + auto lexer = new clang::Lexer(fake_loc, + lang_opts ? *lang_opts : default_opts, + content.begin(), + content.begin(), + content.end()); + lexer->SetCommentRetentionState(!ignore_comments); + impl = lexer; } -void tokenize(llvm::StringRef content, - llvm::unique_function callback, - bool ignoreComments, - const clang::LangOptions* langOpts) { - clang::LangOptions defaultLangOpts; - defaultLangOpts.CPlusPlus = 1; - defaultLangOpts.CPlusPlus26 = 1; - defaultLangOpts.LineComment = !ignoreComments; +Lexer::~Lexer() { + delete static_cast(impl); +} - clang::Lexer lexer(fakeLoc, - langOpts ? *langOpts : defaultLangOpts, - content.begin(), - content.begin(), - content.end()); - lexer.SetCommentRetentionState(!ignoreComments); +void Lexer::lex(Token& token) { + auto lexer = static_cast(impl); - clang::Token token; - while(true) { - lexer.LexFromRawLexer(token); - if(token.is(clang::tok::eof)) { - break; + clang::Token raw_token; + + if(parse_header_name) { + lexer->LexIncludeFilename(raw_token); + } else { + lexer->LexFromRawLexer(raw_token); + } + + token.kind = raw_token.getKind(); + token.is_at_start_of_line = raw_token.isAtStartOfLine(); + token.is_preprocessor_directive = parse_preprocessor_directive; + + auto offset = raw_token.getLocation().getRawEncoding() - fake_loc.getRawEncoding(); + token.range = LocalSourceRange{offset, offset + raw_token.getLength()}; + + if(token.is_at_start_of_line) { + if(token.kind == clang::tok::hash) { + parse_preprocessor_directive = true; + lexer->setParsingPreprocessorDirective(true); } - if(!callback(token)) { - break; + parse_header_name = false; + } else if(parse_preprocessor_directive) { + /// Preprocessor directive token only have one. + parse_preprocessor_directive = false; + + parse_header_name = token.text(content) == "include"; + } +} + +Token Lexer::last() { + return last_token; +} + +Token Lexer::next() { + if(!next_token) { + Token token; + lex(token); + next_token.emplace(token); + } + + return *next_token; +} + +Token Lexer::advance() { + last_token = current_token; + + if(next_token) { + current_token = *next_token; + next_token.reset(); + } else { + Token token; + lex(token); + current_token = token; + } + + return current_token; +} + +Token Lexer::advance_until(clang::tok::TokenKind kind) { + while(true) { + auto token = advance(); + if(token.kind == kind || token.is_eof()) { + return token; } } } diff --git a/src/Compiler/Compilation.cpp b/src/Compiler/Compilation.cpp index 91275f42..266d8430 100644 --- a/src/Compiler/Compilation.cpp +++ b/src/Compiler/Compilation.cpp @@ -4,11 +4,96 @@ #include "Compiler/Diagnostic.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/MultiplexConsumer.h" namespace clice { namespace { +/// A wrapper ast consumer, so that we can cancel the ast parse +class ProxyASTConsumer final : public clang::MultiplexConsumer { +public: + ProxyASTConsumer(std::unique_ptr consumer, + clang::CompilerInstance& instance, + std::vector& top_level_decls, + std::shared_ptr stop) : + clang::MultiplexConsumer(std::move(consumer)), instance(instance), + src_mgr(instance.getSourceManager()), top_level_decls(top_level_decls), stop(stop) { + /// FIXME: We may want to use a more explicit way to judge this. + need_collect = instance.getFrontendOpts().OutputFile.empty(); + } + + void collect_decl(clang::Decl* decl) { + auto location = decl->getLocation(); + if(location.isInvalid()) { + return; + } + + location = src_mgr.getExpansionLoc(location); + auto fid = src_mgr.getFileID(location); + if(fid == src_mgr.getPreambleFileID() || fid == src_mgr.getMainFileID()) { + top_level_decls.push_back(decl); + } + } + + auto HandleTopLevelDecl(clang::DeclGroupRef group) -> bool final { + if(need_collect) { + if(group.isDeclGroup()) { + for(auto decl: group) { + collect_decl(decl); + } + } else { + collect_decl(group.getSingleDecl()); + } + } + + /// TODO: check atomic variable after the parse of each declaration + /// may result in performance issue, benchmark in the future. + if(stop && stop->load()) { + return false; + } + + return clang::MultiplexConsumer::HandleTopLevelDecl(group); + } + +private: + bool need_collect; + clang::CompilerInstance& instance; + clang::SourceManager& src_mgr; + std::vector& top_level_decls; + std::shared_ptr stop; +}; + +class ProxyAction final : public clang::WrapperFrontendAction { +public: + ProxyAction(std::unique_ptr action, + std::shared_ptr stop) : + clang::WrapperFrontendAction(std::move(action)), stop(std::move(stop)) {} + + auto CreateASTConsumer(clang::CompilerInstance& instance, llvm::StringRef file) + -> std::unique_ptr final { + + bool need_collect = instance.getFrontendOpts().OutputFile.empty(); + + return std::make_unique( + WrapperFrontendAction::CreateASTConsumer(instance, file), + instance, + top_level_decls, + std::move(stop)); + } + + /// Make this public. + using clang::WrapperFrontendAction::EndSourceFile; + + auto pop_decls() { + return std::move(top_level_decls); + } + +private: + std::vector top_level_decls; + std::shared_ptr stop; +}; + /// create a `clang::CompilerInvocation` for compilation, it set and reset /// all necessary arguments and flags for clice compilation. auto create_invocation(CompilationParams& params, @@ -44,6 +129,10 @@ auto create_invocation(CompilationParams& params, pp_opts.PrecompiledPreambleBytes = {bound, false}; } + // We don't want to write comment locations into PCM. They are racy and slow + // to read back. We rely on dynamic index for the comments instead. + pp_opts.WriteCommentListToPCH = false; + auto& header_search_opts = invocation->getHeaderSearchOpts(); for(auto& [name, path]: params.pcms) { header_search_opts.PrebuiltModuleFiles.try_emplace(name.str(), std::move(path)); @@ -59,8 +148,10 @@ auto create_invocation(CompilationParams& params, return invocation; } -template -CompilationResult run_clang(CompilationParams& params, const Adjuster& adjuster) { +template +CompilationResult run_clang(CompilationParams& params, + const auto& before_execute, + const auto& after_execute) { auto diagnostics = params.diagnostics ? std::move(params.diagnostics) : std::make_shared>(); auto diagnostic_engine = @@ -88,9 +179,9 @@ CompilationResult run_clang(CompilationParams& params, const Adjuster& adjuster) } /// Adjust the compiler instance, for example, set preamble or modules. - adjuster(*instance); + before_execute(*instance); - std::unique_ptr action = std::make_unique(); + auto action = std::make_unique(std::make_unique(), params.stop); if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { return std::unexpected("Fail to begin source file"); @@ -129,6 +220,13 @@ CompilationResult run_clang(CompilationParams& params, const Adjuster& adjuster) return std::unexpected("Fail to build PCH or PCM, error occurs in compilation."); } + /// Check whether the compilation is canceled, if so we think + /// it is an error. + if(params.stop && params.stop->load()) { + action->EndSourceFile(); + return std::unexpected("Compilation is canceled."); + } + std::optional tok_buf; if(tok_collector) { tok_buf = std::move(*tok_collector).consume(); @@ -142,6 +240,8 @@ CompilationResult run_clang(CompilationParams& params, const Adjuster& adjuster) resolver.emplace(instance->getSema()); } + auto top_level_decls = action->pop_decls(); + auto impl = new CompilationUnit::Impl{ .interested = pp.getSourceManager().getMainFileID(), .src_mgr = instance->getSourceManager(), @@ -153,57 +253,72 @@ CompilationResult run_clang(CompilationParams& params, const Adjuster& adjuster) .pathCache = llvm::DenseMap(), .symbolHashCache = llvm::DenseMap(), .diagnostics = diagnostics, + .top_level_decls = std::move(top_level_decls), }; - return CompilationUnit(CompilationUnit::SyntaxOnly, impl); + CompilationUnit unit(CompilationUnit::SyntaxOnly, impl); + after_execute(unit); + return unit; } } // namespace CompilationResult preprocess(CompilationParams& params) { - return run_clang(params, [](auto&) {}); + return run_clang(params, [](auto&) {}, [](auto&) {}); } CompilationResult compile(CompilationParams& params) { - return run_clang(params, [](auto&) {}); + return run_clang(params, [](auto&) {}, [](auto&) {}); } CompilationResult compile(CompilationParams& params, PCHInfo& out) { - assert(!params.outPath.empty() && "PCH file path cannot be empty"); + assert(!params.output_file.empty() && "PCH file path cannot be empty"); - out.path = params.outPath.str(); - /// out.preamble = params.content.substr(0, *params.bound); /// out.command = params.arguments.str(); /// FIXME: out.deps = info->deps(); - return run_clang(params, [&](clang::CompilerInstance& instance) { - /// Set options to generate PCH. - instance.getFrontendOpts().OutputFile = params.outPath.str(); - instance.getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; - instance.getPreprocessorOpts().GeneratePreamble = true; - instance.getLangOpts().CompilingPCH = true; - }); + return run_clang( + params, + [&](clang::CompilerInstance& instance) { + /// Set options to generate PCH. + instance.getFrontendOpts().OutputFile = params.output_file.str(); + instance.getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; + instance.getPreprocessorOpts().GeneratePreamble = true; + + // We don't want to write comment locations into PCH. They are racy and slow + // to read back. We rely on dynamic index for the comments instead. + instance.getPreprocessorOpts().WriteCommentListToPCH = false; + + instance.getLangOpts().CompilingPCH = true; + }, + [&](CompilationUnit& unit) { + out.path = params.output_file.str(); + out.preamble = unit.interested_content(); + }); } CompilationResult compile(CompilationParams& params, PCMInfo& out) { - for(auto& [name, path]: params.pcms) { - out.mods.emplace_back(name); - } - out.path = params.outPath.str(); - + assert(!params.output_file.empty() && "PCM file path cannot be empty"); return run_clang( params, [&](clang::CompilerInstance& instance) { /// Set options to generate PCH. - instance.getFrontendOpts().OutputFile = params.outPath.str(); + instance.getFrontendOpts().OutputFile = params.output_file.str(); instance.getFrontendOpts().ProgramAction = clang::frontend::GenerateReducedModuleInterface; + out.srcPath = instance.getFrontendOpts().Inputs[0].getFile(); + }, + [&](CompilationUnit& unit) { + out.path = params.output_file.str(); + + for(auto& [name, path]: params.pcms) { + out.mods.emplace_back(name); + } }); } CompilationResult complete(CompilationParams& params, clang::CodeCompleteConsumer* consumer) { - auto& [file, offset] = params.completion; /// The location of clang is 1-1 based. @@ -223,13 +338,18 @@ CompilationResult complete(CompilationParams& params, clang::CodeCompleteConsume column += 1; } - return run_clang(params, [&](clang::CompilerInstance& instance) { - /// Set options to run code completion. - instance.getFrontendOpts().CodeCompletionAt.FileName = std::move(file); - instance.getFrontendOpts().CodeCompletionAt.Line = line; - instance.getFrontendOpts().CodeCompletionAt.Column = column; - instance.setCodeCompletionConsumer(consumer); - }); + return run_clang( + params, + [&](clang::CompilerInstance& instance) { + /// Set options to run code completion. + instance.getFrontendOpts().CodeCompletionAt.FileName = std::move(file); + instance.getFrontendOpts().CodeCompletionAt.Line = line; + instance.getFrontendOpts().CodeCompletionAt.Column = column; + instance.setCodeCompletionConsumer(consumer); + }, + [&](CompilationUnit& unit) { + /// + }); } } // namespace clice diff --git a/src/Compiler/CompilationUnit.cpp b/src/Compiler/CompilationUnit.cpp index 2168febc..b47423b1 100644 --- a/src/Compiler/CompilationUnit.cpp +++ b/src/Compiler/CompilationUnit.cpp @@ -37,23 +37,23 @@ auto CompilationUnit::decompose_range(clang::SourceRange range) auto [fid, offset] = decompose_location(begin); return { fid, - {offset, offset + getTokenLength(impl->src_mgr, end)} + {offset, offset + token_length(end)} }; } else { - auto [beginFID, beginOffset] = decompose_location(begin); - auto [endFID, endOffset] = decompose_location(end); - if(beginFID == endFID) { - return { - beginFID, - {beginOffset, endOffset + getTokenLength(impl->src_mgr, end)} - }; + auto [begin_fid, begin_offset] = decompose_location(begin); + auto [end_fid, end_offset] = decompose_location(end); + + if(begin_fid == end_fid) { + end_offset += token_length(end); } else { - auto content = file_content(beginFID); - return { - beginFID, - {beginOffset, static_cast(content.size())} - }; + auto content = file_content(begin_fid); + end_offset = content.size(); } + + return { + begin_fid, + {begin_offset, end_offset} + }; } } @@ -151,8 +151,12 @@ auto CompilationUnit::expanded_tokens(clang::SourceRange range) return impl->buffer->expandedTokens(range); } +auto CompilationUnit::token_length(clang::SourceLocation location) -> std::uint32_t { + return clang::Lexer::MeasureTokenLength(location, impl->src_mgr, impl->instance->getLangOpts()); +} + auto CompilationUnit::token_spelling(clang::SourceLocation location) -> llvm::StringRef { - return getTokenSpelling(impl->src_mgr, location); + return llvm::StringRef(impl->src_mgr.getCharacterData(location), token_length(location)); } auto CompilationUnit::module_name() -> llvm::StringRef { @@ -179,7 +183,7 @@ std::vector CompilationUnit::deps() { } } - for(auto& hasInclude: diretive.hasIncludes) { + for(auto& hasInclude: diretive.has_includes) { if(hasInclude.fid.isValid()) { deps.try_emplace(file_path(hasInclude.fid)); } @@ -211,7 +215,7 @@ index::SymbolID CompilationUnit::getSymbolID(const clang::NamedDecl* decl) { index::SymbolID CompilationUnit::getSymbolID(const clang::MacroInfo* macro) { std::uint64_t hash; - auto name = getTokenSpelling(impl->src_mgr, macro->getDefinitionLoc()); + auto name = token_spelling(macro->getDefinitionLoc()); auto iter = impl->symbolHashCache.find(macro); if(iter != impl->symbolHashCache.end()) { hash = iter->second; @@ -233,6 +237,10 @@ auto CompilationUnit::diagnostics() -> llvm::ArrayRef { return *impl->diagnostics; } +auto CompilationUnit::top_level_decls() -> llvm::ArrayRef { + return impl->top_level_decls; +} + const llvm::DenseSet& CompilationUnit::files() { if(impl->allFiles.empty()) { /// FIXME: handle preamble and embed file id. diff --git a/src/Compiler/CompilationUnitImpl.h b/src/Compiler/CompilationUnitImpl.h index 59b4f1c5..577d403c 100644 --- a/src/Compiler/CompilationUnitImpl.h +++ b/src/Compiler/CompilationUnitImpl.h @@ -39,9 +39,9 @@ struct CompilationUnit::Impl { llvm::BumpPtrAllocator pathStorage; - std::vector top_level_decls; - std::shared_ptr> diagnostics; + + std::vector top_level_decls; }; } // namespace clice diff --git a/src/Compiler/Diagnostic.cpp b/src/Compiler/Diagnostic.cpp index 15bff09b..56abba50 100644 --- a/src/Compiler/Diagnostic.cpp +++ b/src/Compiler/Diagnostic.cpp @@ -175,7 +175,7 @@ auto diagnostic_range(const clang::Diagnostic& diagnostic, const clang::LangOpti auto [end_fid, end_offset] = src_mgr.getDecomposedLoc(end); if(range.isTokenRange()) { - end_offset += getTokenLength(src_mgr, end); + end_offset += clang::Lexer::MeasureTokenLength(end, src_mgr, options); } if(end_fid == fid && end_offset >= offset) { @@ -187,7 +187,7 @@ auto diagnostic_range(const clang::Diagnostic& diagnostic, const clang::LangOpti } /// Use token range. - auto end_offset = offset + getTokenLength(src_mgr, location); + auto end_offset = offset + clang::Lexer::MeasureTokenLength(location, src_mgr, options); return std::pair{ fid, LocalSourceRange{offset, end_offset} diff --git a/src/Compiler/Directive.cpp b/src/Compiler/Directive.cpp index f386d9d6..cfe55eac 100644 --- a/src/Compiler/Directive.cpp +++ b/src/Compiler/Directive.cpp @@ -83,7 +83,7 @@ public: directives[prevFID].includes.emplace_back(Include{ .fid = {}, .location = includeTok.getLocation(), - .fileNameRange = filenameRange.getAsRange(), + .filename_range = filenameRange.getAsRange(), }); } @@ -139,7 +139,7 @@ public: fid = SM.translateFile(*file); } - directives[SM.getFileID(location)].hasIncludes.emplace_back(fid, location); + directives[SM.getFileID(location)].has_includes.emplace_back(fid, location); } void PragmaDirective(clang::SourceLocation Loc, diff --git a/src/Compiler/Preamble.cpp b/src/Compiler/Preamble.cpp index aa682100..086967ac 100644 --- a/src/Compiler/Preamble.cpp +++ b/src/Compiler/Preamble.cpp @@ -5,8 +5,8 @@ namespace clice { -std::uint32_t computePreambleBound(llvm::StringRef content) { - auto result = computePreambleBounds(content); +std::uint32_t compute_preamble_bound(llvm::StringRef content) { + auto result = compute_preamble_bounds(content); if(result.empty()) { return 0; } else { @@ -14,81 +14,46 @@ std::uint32_t computePreambleBound(llvm::StringRef content) { } } -std::vector computePreambleBounds(llvm::StringRef content) { +std::vector compute_preamble_bounds(llvm::StringRef content) { std::vector result; - /// The endLoc of the last token we iterate over. - clang::SourceLocation end_loc; - /// Whether the token is after `#`. - bool isAfterHash = false; - /// Whether the token is in the directive line. - bool isInHeader = false; - /// Whether the token is after `module`. - bool isAfterModule = false; + Lexer lexer(content, true, nullptr, false); - auto addResult = [&](const clang::SourceLocation end_loc) { - if(end_loc.isInvalid()) { - log::fatal("end_loc is invalid, but should be valid"); - return; + while(true) { + auto token = lexer.advance(); + if(token.is_eof()) { + break; } - auto offset = end_loc.getRawEncoding() - fakeLoc.getRawEncoding(); - if(result.empty() || result.back() != offset) { - result.emplace_back(offset); - } - }; - tokenize(content, [&](const clang::Token& token) { - if(token.isAtStartOfLine()) { - if(isInHeader) { - /// If the last line is `#include`, add it to result. - addResult(end_loc); - isInHeader = false; - } + if(token.is_at_start_of_line) { + if(token.kind == clang::tok::hash) { + /// For preprocessor directive, consume the whole directive. + lexer.advance_until(clang::tok::eod); + auto last = lexer.last(); - if(token.is(clang::tok::hash)) { - /// If we encounter a `#` at the start of a line, it must be a directive. - isAfterHash = true; - return true; - } else if(token.is(clang::tok::raw_identifier) && - token.getRawIdentifier() == "module") { + /// Append the token before the eod. + result.push_back(last.range.end); + } else if(token.is_identifier() && token.text(content) == "module") { /// If we encounter a module keyword at the start of a line, it may be /// a module declaration or global module fragment. - isAfterModule = true; - return true; - } + auto next = lexer.next(); - /// If the first token is either `#` or `module`, We stop at here. - return false; - } + if(next.kind == clang::tok::semi) { + /// If next token is `;`, it is a global module fragment. + /// we just continue. + lexer.advance(); - if(isAfterHash) { - isAfterHash = false; - if(token.is(clang::tok::raw_identifier) && token.getRawIdentifier() == "include") { - isInHeader = true; - } - } - - if(isAfterModule) { - if(token.is(clang::tok::semi)) { - isAfterModule = false; + /// Append it to bounds. + result.push_back(next.range.end); + } else { + break; + } } else { - /// If we meet a module declaration, directly return. - return false; + break; } } - - end_loc = token.getEndLoc(); - - return true; - }); - - /// Add last location. - if(end_loc.isInvalid()) { - return std::vector{}; } - addResult(end_loc); - return result; } diff --git a/src/Feature/DocumentLink.cpp b/src/Feature/DocumentLink.cpp index c7d8207f..29080d00 100644 --- a/src/Feature/DocumentLink.cpp +++ b/src/Feature/DocumentLink.cpp @@ -14,39 +14,27 @@ index::Shared indexDocumentLink(CompilationUnit& unit) { for(auto& [fid, diretives]: unit.directives()) { for(auto& include: diretives.includes) { - auto [_, range] = unit.decompose_range(include.fileNameRange); + auto [_, range] = unit.decompose_range(include.filename_range); result[fid].emplace_back(range, unit.file_path(include.fid).str()); } auto content = unit.file_content(fid); - for(auto& hasInclude: diretives.hasIncludes) { + for(auto& has_include: diretives.has_includes) { /// If the include path is empty, skip it. - if(hasInclude.fid.isInvalid()) { + if(has_include.fid.isInvalid()) { continue; } - auto location = hasInclude.location; + auto location = has_include.location; auto [_, offset] = unit.decompose_location(location); - auto subContent = content.substr(offset); + /// FIXME: handle incomplete code, the <> or "" may not be in pair. + auto sub_content = content.substr(offset); + char c = sub_content[0] == '<' ? '>' : '"'; + std::uint32_t end_offset = offset + sub_content.find_first_of(c, 1) + 1; - bool isFirst = true; - std::uint32_t endOffset = offset; - tokenize(subContent, [&](const clang::Token& token) { - if(token.is(clang::tok::r_paren) || (!isFirst && token.isAtStartOfLine())) { - return false; - } - - if(isFirst) { - isFirst = false; - } - - endOffset = offset + token.getEndLoc().getRawEncoding() - fakeLoc.getRawEncoding(); - return true; - }); - - result[fid].emplace_back(LocalSourceRange{offset, endOffset}, - unit.file_path(hasInclude.fid).str()); + result[fid].emplace_back(LocalSourceRange{offset, end_offset}, + unit.file_path(has_include.fid).str()); } } diff --git a/src/Feature/FoldingRange.cpp b/src/Feature/FoldingRange.cpp index 60ebe42e..4ed5e6c1 100644 --- a/src/Feature/FoldingRange.cpp +++ b/src/Feature/FoldingRange.cpp @@ -271,7 +271,7 @@ private: case Condition::BranchKind::Else: { if(!stack.empty()) { auto last = stack.pop_back_val(); - addRange({last->conditionRange.getEnd(), cond.loc}, + addRange({last->condition_range.getEnd(), cond.loc}, FoldingRangeKind::ConditionDirective, ""); } @@ -286,7 +286,7 @@ private: // For a directive without condition range e.g #else // its condition range is invalid. - if(last->conditionRange.isValid()) { + if(last->condition_range.isValid()) { /// collect({last->conditionRange.getBegin(), cond.loc}, {0, -1}); } else { /// collect({last->loc, cond.loc}, diff --git a/src/Feature/SemanticToken.cpp b/src/Feature/SemanticToken.cpp index facb2b01..fc34ae24 100644 --- a/src/Feature/SemanticToken.cpp +++ b/src/Feature/SemanticToken.cpp @@ -30,7 +30,7 @@ public: /// TODO: Add more modifiers. - addToken(location, SymbolKind::from(decl), modifiers); + add_token(location, SymbolKind::from(decl), modifiers); } void handleMacroOccurrence(const clang::MacroInfo* def, @@ -44,7 +44,7 @@ public: modifiers |= SymbolModifiers::Declaration; } - addToken(location, SymbolKind::Macro, modifiers); + add_token(location, SymbolKind::Macro, modifiers); } /// FIXME: Handle module name occurrence. @@ -55,21 +55,21 @@ public: auto [begin, end] = range; if(auto FA = clang::dyn_cast(attr)) { assert(begin == end && "Invalid range"); - addToken(begin, SymbolKind::Keyword, {}); + add_token(begin, SymbolKind::Keyword, {}); } else if(auto OA = clang::dyn_cast(attr)) { assert(begin == end && "Invalid range"); - addToken(begin, SymbolKind::Keyword, {}); + add_token(begin, SymbolKind::Keyword, {}); } } - auto buildForFile() { + auto build_for_file() { highlight(unit.interested_file()); run(); merge(result); return std::move(result); } - auto buildForIndex() { + auto build_for_index() { for(auto fid: unit.files()) { highlight(fid); } @@ -84,15 +84,12 @@ public: } public: - void addToken(clang::FileID fid, const clang::Token& token, SymbolKind kind) { - auto offset = token.getLocation().getRawEncoding() - fakeLoc.getRawEncoding(); - LocalSourceRange range{offset, offset + token.getLength()}; - + void add_token(clang::FileID fid, Token& token, SymbolKind kind) { auto& tokens = interestedOnly ? result : sharedResult[fid]; - tokens.emplace_back(range, kind, SymbolModifiers()); + tokens.emplace_back(token.range, kind, SymbolModifiers()); } - void addToken(clang::SourceLocation location, SymbolKind kind, SymbolModifiers modifiers) { + void add_token(clang::SourceLocation location, SymbolKind kind, SymbolModifiers modifiers) { if(location.isMacroID()) { auto spelling = unit.spelling_location(location); auto expansion = unit.expansion_location(location); @@ -120,107 +117,87 @@ public: /// Render semantic tokens for file through raw lexer. void highlight(clang::FileID fid) { auto content = unit.file_content(fid); - auto& langOpts = unit.lang_options(); - - /// Whether the token is after `#`. - bool isAfterHash = false; - /// Whether the token is in the header name. - bool isInHeader = false; - /// Whether the token is in the directive line. - bool isInDirectiveLine = false; + auto& lang_opts = unit.lang_options(); /// Use to distinguish whether the token is in a keyword. - clang::IdentifierTable identifierTable(langOpts); + clang::IdentifierTable identifierTable(lang_opts); + + Lexer lexer(content, false, &lang_opts); + + while(true) { + Token token = lexer.advance(); + if(token.is_eof()) { + break; + } - auto callback = [&](const clang::Token& token) -> bool { SymbolKind kind = SymbolKind::Invalid; - - /// Clear the all states. - if(token.isAtStartOfLine()) { - isInHeader = false; - isInDirectiveLine = false; - } - - if(isInHeader) { - addToken(fid, token, SymbolKind::Header); - return true; - } - - switch(token.getKind()) { - case clang::tok::comment: { - kind = SymbolKind::Comment; - break; - } - - case clang::tok::numeric_constant: { - kind = SymbolKind::Number; - break; - } - - case clang::tok::char_constant: - case clang::tok::wide_char_constant: - case clang::tok::utf8_char_constant: - case clang::tok::utf16_char_constant: - case clang::tok::utf32_char_constant: { - kind = SymbolKind::Character; - break; - } - - case clang::tok::string_literal: { - kind = SymbolKind::String; - break; - } - - case clang::tok::wide_string_literal: - case clang::tok::utf8_string_literal: - case clang::tok::utf16_string_literal: - case clang::tok::utf32_string_literal: { - kind = SymbolKind::String; - break; - } - - case clang::tok::hash: { - if(token.isAtStartOfLine()) { - isAfterHash = true; - isInDirectiveLine = true; - kind = SymbolKind::Directive; + if(token.is_at_start_of_line && token.kind == clang::tok::hash) { + kind = SymbolKind::Directive; + } else if(token.is_preprocessor_directive) { + kind = SymbolKind::Directive; + } else { + switch(token.kind) { + case clang::tok::comment: { + kind = SymbolKind::Comment; + break; } - break; - } - case clang::tok::raw_identifier: { - auto spelling = token.getRawIdentifier(); - if(isAfterHash) { - isAfterHash = false; - isInHeader = (spelling == "include"); - kind = SymbolKind::Directive; - } else if(isInHeader) { + case clang::tok::numeric_constant: { + kind = SymbolKind::Number; + break; + } + + case clang::tok::char_constant: + case clang::tok::wide_char_constant: + case clang::tok::utf8_char_constant: + case clang::tok::utf16_char_constant: + case clang::tok::utf32_char_constant: { + kind = SymbolKind::Character; + break; + } + + case clang::tok::string_literal: { + kind = SymbolKind::String; + break; + } + + case clang::tok::wide_string_literal: + case clang::tok::utf8_string_literal: + case clang::tok::utf16_string_literal: + case clang::tok::utf32_string_literal: { + kind = SymbolKind::String; + break; + } + + case clang::tok::header_name: { kind = SymbolKind::Header; - } else if(isInDirectiveLine) { - if(spelling == "defined") { - kind = SymbolKind::Directive; + break; + } + + case clang::tok::raw_identifier: { + auto last = lexer.last(); + if(last.is_preprocessor_directive && last.text(content) == "define") { + kind = SymbolKind::Macro; + break; } - } else { + + auto spelling = token.text(content); /// Check whether the identifier is a keyword. - if(auto& II = identifierTable.get(spelling); II.isKeyword(langOpts)) { + if(auto& II = identifierTable.get(spelling); II.isKeyword(lang_opts)) { kind = SymbolKind::Keyword; } } - } - default: { - break; + default: { + break; + } } } if(kind != SymbolKind::Invalid) { - addToken(fid, token, kind); + add_token(fid, token, kind); } - - return true; - }; - - tokenize(content, callback, false, &langOpts); + } } void resolve(SemanticToken& last, const SemanticToken& current) { @@ -268,7 +245,7 @@ public: } // namespace -SemanticTokens semanticTokens(CompilationUnit& unit) { +SemanticTokens semantic_tokens(CompilationUnit& unit) { SemanticTokensCollector collector(unit, true); collector.highlight(unit.interested_file()); collector.run(); diff --git a/src/Server/Document.cpp b/src/Server/Document.cpp index 5b1e7489..6c04242c 100644 --- a/src/Server/Document.cpp +++ b/src/Server/Document.cpp @@ -25,7 +25,7 @@ async::Task Server::add_document(std::string path, std::string conten } async::Task<> Server::build_pch(std::string path, std::string content) { - auto bound = computePreambleBound(content); + auto bound = compute_preamble_bound(content); auto openFile = &opening_files[path]; bool outdated = true; @@ -58,7 +58,7 @@ async::Task<> Server::build_pch(std::string path, std::string content) { diagnostics->clear(); CompilationParams params; - params.outPath = path::join(config::cache.dir, path::filename(path) + ".pch"); + params.output_file = path::join(config::cache.dir, path::filename(path) + ".pch"); params.arguments = server.database.get_command(path, true, true).arguments; params.diagnostics = diagnostics; params.add_remapped_file(path, content, bound); @@ -155,6 +155,9 @@ async::Task<> Server::build_ast(std::string path, std::string content) { if(!ast) { /// FIXME: Fails needs cancel waiting tasks. log::warn("Building AST fails for {}, Beacuse: {}", path, ast.error()); + for(auto& diagnostic: *file->diagnostics) { + log::warn("{}", diagnostic.message); + } co_return; } diff --git a/src/Server/Feature.cpp b/src/Server/Feature.cpp index 36a93ca8..b92455dc 100644 --- a/src/Server/Feature.cpp +++ b/src/Server/Feature.cpp @@ -18,7 +18,7 @@ async::Task Server::on_semantic_token(proto::SemanticTokensParams p } co_return co_await async::submit([kind = this->kind, &ast] { - auto tokens = feature::semanticTokens(*ast); + auto tokens = feature::semantic_tokens(*ast); return proto::to_json(kind, ast->interested_content(), tokens); }); } diff --git a/tests/unit/AST/Resolver.cpp b/tests/unit/AST/Resolver.cpp index 0dae02d8..db89c044 100644 --- a/tests/unit/AST/Resolver.cpp +++ b/tests/unit/AST/Resolver.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "clang/AST/RecursiveASTVisitor.h" namespace clice::testing { @@ -38,7 +38,7 @@ struct InputFinder : clang::RecursiveASTVisitor { struct TemplateResolver : TestFixture { void run(llvm::StringRef code, LocationChain chain = LocationChain()) { - addMain("main.cpp", code); + add_main("main.cpp", code); compile(); InputFinder finder(*unit); diff --git a/tests/unit/AST/Selection.cpp b/tests/unit/AST/Selection.cpp index 375b4945..675c46dd 100644 --- a/tests/unit/AST/Selection.cpp +++ b/tests/unit/AST/Selection.cpp @@ -1,6 +1,6 @@ #include "src/AST/Selection.cpp" -#include "Test/CTest.h" +#include "Test/Tester.h" namespace clice { @@ -48,7 +48,9 @@ void debug(const SelectionTree& tree) { } struct SelectionTester : public Tester { - SelectionTester(llvm::StringRef file, llvm::StringRef content) : Tester(file, content) {} + SelectionTester(llvm::StringRef file, llvm::StringRef content) { + add_main(file, content); + } void expectPreorderSequence(const SelectionTree& tree, llvm::ArrayRef kinds) { @@ -86,8 +88,8 @@ TEST(Selection, VarDeclSelectionBoundary) { std::vector selects; for(int begin = 1; begin <= 2; begin++) { for(int end = 1; end <= 3; end++) { - uint32_t bp = tx.offset(std::format("b{}", begin)); - uint32_t ep = tx.offset(std::format("e{}", end)); + uint32_t bp = tx["main.cpp", std::format("b{}", begin)]; + uint32_t ep = tx["main.cpp", std::format("e{}", end)]; selects.push_back({bp, ep}); } } @@ -116,8 +118,8 @@ TEST(Selection, VarDeclSelectionBoundary) { std::vector selects; for(int i = 1; i <= 3; ++i) { - uint32_t bp = tx.offset(std::format("b{}", i)); - uint32_t ep = tx.offset(std::format("e{}", i)); + uint32_t bp = tx["main.cpp", std::format("b{}", i)]; + uint32_t ep = tx["main.cpp", std::format("e{}", i)]; selects.push_back({bp, ep}); } @@ -145,8 +147,8 @@ TEST(Selection, VarDeclSelectionBoundary) { std::vector selects; for(int i = 1; i <= 4; ++i) { for(int j = 1; j <= 2; ++j) { - uint32_t bp = tx.offset(std::format("b{}", i)); - uint32_t ep = tx.offset(std::format("e{}", j)); + uint32_t bp = tx["main.cpp", std::format("b{}", i)]; + uint32_t ep = tx["main.cpp", std::format("e{}", j)]; selects.push_back({bp, ep}); } } @@ -180,8 +182,8 @@ TEST(Selection, VarDeclSelectionBoundary) { SelectionTester tx("main.cpp", code); tx.compile(); - uint32_t bp = tx.offset("b"); - uint32_t ep = tx.offset("e"); + uint32_t bp = tx["main.cpp", "b"]; + uint32_t ep = tx["main.cpp", "e"]; auto& unit = *tx.unit; auto tokens = unit.spelled_tokens(unit.interested_file()); @@ -205,8 +207,8 @@ TEST(Selection, ParmVarDeclBoundary) { std::vector selects; for(int begin = 1; begin <= 2; begin++) { for(int end = 1; end <= 2; end++) { - uint32_t bp = tx.offset(std::format("b{}", begin)); - uint32_t ep = tx.offset(std::format("e{}", end)); + uint32_t bp = tx["main.cpp", std::format("b{}", begin)]; + uint32_t ep = tx["main.cpp", std::format("e{}", end)]; selects.push_back({bp, ep}); } } @@ -233,8 +235,8 @@ TEST(Selection, ParmVarDeclBoundary) { SelectionTester tx("main.cpp", code); tx.compile(); - auto bp = tx.offset("b1"); - auto ep = tx.offset("e1"); + auto bp = tx["main.cpp", "b1"]; + auto ep = tx["main.cpp", "e1"]; auto& unit = *tx.unit; auto tokens = unit.spelled_tokens(unit.interested_file()); @@ -261,8 +263,8 @@ namespace test { auto& unit = *tx.unit; - uint32_t begin = tx.offset("stmt_begin"); - uint32_t end = tx.offset("stmt_end"); + uint32_t begin = tx["main.cpp", "stmt_begin"]; + uint32_t end = tx["main.cpp", "stmt_end"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -296,8 +298,8 @@ namespace test { auto& unit = *tx.unit; - uint32_t begin = tx.offset("multi_begin"); - uint32_t end = tx.offset("multi_end"); + uint32_t begin = tx["main.cpp", "multi_begin"]; + uint32_t end = tx["main.cpp", "multi_end"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -331,8 +333,8 @@ $(class_begin)class Test { auto& unit = *tx.unit; - uint32_t begin = tx.offset("class_begin"); - uint32_t end = tx.offset("class_end"); + uint32_t begin = tx["main.cpp", "class_begin"]; + uint32_t end = tx["main.cpp", "class_end"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -361,8 +363,8 @@ class Test { auto& unit = *tx.unit; - uint32_t begin = tx.offset("begin"); - uint32_t end = tx.offset("end"); + uint32_t begin = tx["main.cpp", "begin"]; + uint32_t end = tx["main.cpp", "end"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -391,8 +393,8 @@ void f(int& x){ auto& unit = *tx.unit; { - uint32_t begin = tx.offset("begin1"); - uint32_t end = tx.offset("end1"); + uint32_t begin = tx["main.cpp", "begin1"]; + uint32_t end = tx["main.cpp", "end1"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -414,8 +416,8 @@ void f(int& x){ } { - uint32_t begin = tx.offset("begin2"); - uint32_t end = tx.offset("end2"); + uint32_t begin = tx["main.cpp", "begin2"]; + uint32_t end = tx["main.cpp", "end2"]; auto tokens = unit.spelled_tokens(unit.interested_file()); auto [left, right] = SelectionBuilder::selectionBound(tokens, {begin, end}, unit); @@ -451,8 +453,8 @@ class Test { std::vector b12_e123; for(int begin = 1; begin <= 2; begin++) { for(int end = 1; end <= 3; end++) { - uint32_t bp = tx.offset(std::format("b{}", begin)); - uint32_t ep = tx.offset(std::format("e{}", end)); + uint32_t bp = tx["main.cpp", std::format("b{}", begin)]; + uint32_t ep = tx["main.cpp", std::format("e{}", end)]; b12_e123.push_back({bp, ep}); } } @@ -479,8 +481,8 @@ class Test { std::vector b3_e123; for(int begin = 3; begin <= 3; begin++) { for(int end = 1; end <= 3; end++) { - uint32_t bp = tx.offset(std::format("b{}", begin)); - uint32_t ep = tx.offset(std::format("e{}", end)); + uint32_t bp = tx["main.cpp", std::format("b{}", begin)]; + uint32_t ep = tx["main.cpp", std::format("e{}", end)]; b3_e123.push_back({bp, ep}); } } diff --git a/tests/unit/AST/SourceCode.cpp b/tests/unit/AST/SourceCode.cpp index 787591d7..d2b886fe 100644 --- a/tests/unit/AST/SourceCode.cpp +++ b/tests/unit/AST/SourceCode.cpp @@ -3,19 +3,9 @@ namespace clice::testing { -TEST(SourceCode, tokenize) { - std::size_t count = 0; - - /// Test breaking the loop. - tokenize("int x = 1;", [&](const clang::Token& token) { - count++; - return false; - }); - - EXPECT_EQ(count, 1); - +TEST(SourceCode, IgnoreComments) { /// Test all tokens. - count = 0; + std::size_t count = 0; std::vector kinds = { clang::tok::raw_identifier, @@ -25,17 +15,22 @@ TEST(SourceCode, tokenize) { clang::tok::semi, }; - tokenize("int x = 1; // comment", [&](const clang::Token& token) { - if(token.is(clang::tok::eof)) [[unlikely]] { - return false; + { + /// Test ignore comments. + Lexer lexer("int x = 1; // comment", true); + + while(true) { + Token token = lexer.advance(); + if(token.is_eof()) { + break; + } + + EXPECT_EQ(token.kind, kinds[count]); + count += 1; } - EXPECT_EQ(token.getKind(), kinds[count]); - count++; - return true; - }); - - EXPECT_EQ(count, 5); + EXPECT_EQ(count, 5); + } /// Test retain comments. count = 0; @@ -49,20 +44,46 @@ TEST(SourceCode, tokenize) { clang::tok::comment, }; - tokenize( - "int x = 1; /// comment", - [&](const clang::Token& token) { - if(token.is(clang::tok::eof)) [[unlikely]] { - return false; + { + /// Test retain comments. + Lexer lexer("int x = 1; // comment", false); + + while(true) { + Token token = lexer.advance(); + if(token.is_eof()) { + break; } - EXPECT_EQ(token.getKind(), kinds[count]); - count++; - return true; - }, - false); + EXPECT_EQ(token.kind, kinds[count]); + count += 1; + } - EXPECT_EQ(count, 6); + EXPECT_EQ(count, 6); + } +} + +TEST(SourceCode, LexInclude) { + /// TODO: test eod + /// test multiple lines macros. + + Lexer lexer(R"( +#include +#include "gtest/test.h" +module; +int x = 1; +)", + true, + nullptr, + false); + + // while(true) { + // Token token = lexer.advance(); + // if(token.is_eof()) { + // break; + // } + // + // println("kind: {}", token.name()); + //} } } // namespace clice::testing diff --git a/tests/unit/Compiler/Compiler.cpp b/tests/unit/Compiler/Compiler.cpp index 66667c65..7227e821 100644 --- a/tests/unit/Compiler/Compiler.cpp +++ b/tests/unit/Compiler/Compiler.cpp @@ -1,4 +1,5 @@ -#include "Test/Test.h" +#include +#include "Test/Tester.h" #include "Compiler/Compilation.h" #include "Support/FileSystem.h" @@ -6,11 +7,60 @@ namespace clice::testing { namespace { -TEST(Compiler, buildAST) {} +TEST(Compiler, TopLevelDecls) { + Tester tester; -TEST(Compiler, buildPCM) {} + llvm::StringRef content = R"( +#include -TEST(Compiler, codeCompleteAt) {} +int x = 1; + +void foo {} + +namespace foo2 { + int y = 2; + int z = 3; +} + +struct Bar { + int x; + int y; +}; +)"; + + tester.add_main("main.cpp", content); + tester.compile_with_pch(); + + EXPECT_EQ(tester.unit->top_level_decls().size(), 4); +} + +TEST(Compiler, StopCompilation) { + std::shared_ptr stop = std::make_shared(false); + + Tester tester; + tester.params.stop = stop; + + llvm::StringRef content = R"( +#include +#include +#include +#include +#include +#include +)"; + tester.add_main("main.cpp", content); + + bool result = true; + + std::thread thread([&]() { result = tester.compile_with_pch(); }); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + stop->store(true); + + thread.join(); + + EXPECT_FALSE(result); +} } // namespace diff --git a/tests/unit/Compiler/Diagnostic.cpp b/tests/unit/Compiler/Diagnostic.cpp index 07b3f163..8bcb9506 100644 --- a/tests/unit/Compiler/Diagnostic.cpp +++ b/tests/unit/Compiler/Diagnostic.cpp @@ -104,7 +104,7 @@ TEST(Diagnostic, PCHError) { /// Any error in compilation will result in failure on generating PCH or PCM. CompilationParams params; params.arguments = {"clang++", "main.cpp"}; - params.outPath = "fake.pch"; + params.output_file = "fake.pch"; params.add_remapped_file("main.cpp", R"( void foo() {} void foo() {} diff --git a/tests/unit/Compiler/Directive.cpp b/tests/unit/Compiler/Directive.cpp index 69197afe..82c62c35 100644 --- a/tests/unit/Compiler/Directive.cpp +++ b/tests/unit/Compiler/Directive.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" namespace clice::testing { @@ -11,66 +11,75 @@ struct Directive : ::testing::Test, Tester { llvm::ArrayRef macros; llvm::ArrayRef pragmas; + using Self = Directive; + void run(const char* standard = "-std=c++20") { Tester::compile("-std=c++23"); auto fid = unit->interested_file(); includes = unit->directives()[fid].includes; - hasIncludes = unit->directives()[fid].hasIncludes; + hasIncludes = unit->directives()[fid].has_includes; conditions = unit->directives()[fid].conditions; macros = unit->directives()[fid].macros; pragmas = unit->directives()[fid].pragmas; } - void EXPECT_INCLUDE(std::size_t index, + void EXPECT_INCLUDE(this Self& self, + std::size_t index, llvm::StringRef position, llvm::StringRef path, LocationChain chain = LocationChain()) { - auto& include = includes[index]; - auto [_, offset] = unit->decompose_location(include.location); - EXPECT_EQ(offset, this->offset(position), chain); - EXPECT_EQ(include.skipped ? "" : unit->file_path(include.fid), path, chain); + auto& include = self.includes[index]; + auto [_, offset] = self.unit->decompose_location(include.location); + EXPECT_EQ(offset, self["main.cpp", position], chain); + EXPECT_EQ(include.skipped ? "" : self.unit->file_path(include.fid), path, chain); } - void EXPECT_HAS_INCLUDE(std::size_t index, + void EXPECT_HAS_INCLUDE(this Self& self, + std::size_t index, llvm::StringRef position, llvm::StringRef path, LocationChain chain = LocationChain()) { - auto& hasInclude = hasIncludes[index]; - auto [_, offset] = unit->decompose_location(hasInclude.location); - EXPECT_EQ(offset, this->offset(position), chain); - EXPECT_EQ(hasInclude.fid.isValid() ? unit->file_path(hasInclude.fid) : "", path, chain); + auto& hasInclude = self.hasIncludes[index]; + auto [_, offset] = self.unit->decompose_location(hasInclude.location); + EXPECT_EQ(offset, self["main.cpp", position], chain); + EXPECT_EQ(hasInclude.fid.isValid() ? self.unit->file_path(hasInclude.fid) : "", + path, + chain); } - void EXPECT_CON(std::size_t index, + void EXPECT_CON(this Self& self, + std::size_t index, Condition::BranchKind kind, llvm::StringRef position, LocationChain chain = LocationChain()) { - auto& condition = conditions[index]; - auto [_, offset] = unit->decompose_location(condition.loc); + auto& condition = self.conditions[index]; + auto [_, offset] = self.unit->decompose_location(condition.loc); EXPECT_EQ(condition.kind, kind, chain); - EXPECT_EQ(offset, this->offset(position), chain); + EXPECT_EQ(offset, self["main.cpp", position], chain); } - void EXPECT_MACRO(std::size_t index, + void EXPECT_MACRO(this Self& self, + std::size_t index, MacroRef::Kind kind, llvm::StringRef position, LocationChain chain = LocationChain()) { - auto& macro = macros[index]; - auto [_, offset] = unit->decompose_location(macro.loc); + auto& macro = self.macros[index]; + auto [_, offset] = self.unit->decompose_location(macro.loc); EXPECT_EQ(macro.kind, kind, chain); - EXPECT_EQ(offset, this->offset(position), chain); + EXPECT_EQ(offset, self["main.cpp", position], chain); } - void EXPECT_PRAGMA(std::size_t index, + void EXPECT_PRAGMA(this Self& self, + std::size_t index, Pragma::Kind kind, llvm::StringRef position, llvm::StringRef text, LocationChain chain = LocationChain()) { - auto& pragma = pragmas[index]; - auto [_, offset] = unit->decompose_location(pragma.loc); + auto& pragma = self.pragmas[index]; + auto [_, offset] = self.unit->decompose_location(pragma.loc); EXPECT_EQ(pragma.kind, kind, chain); EXPECT_EQ(pragma.stmt, text, chain); - EXPECT_EQ(offset, this->offset(position), chain); + EXPECT_EQ(offset, self["main.cpp", position], chain); } }; @@ -100,15 +109,15 @@ TEST_F(Directive, Include) { #$(5)include "guard_macro.h" )cpp"; - addMain("main.cpp", main); + add_main("main.cpp", main); auto ptest = path::join(".", "test.h"); auto ppragma_once = path::join(".", "pragma_once.h"); auto pguard_macro = path::join(".", "guard_macro.h"); - addFile(ptest, test); - addFile(ppragma_once, pragma_once); - addFile(pguard_macro, guard_macro); + add_file(ptest, test); + add_file(ppragma_once, pragma_once); + add_file(pguard_macro, guard_macro); run(); EXPECT_EQ(includes.size(), 6); @@ -133,10 +142,10 @@ TEST_F(Directive, HasInclude) { #endif )cpp"; - addMain("main.cpp", main); + add_main("main.cpp", main); auto path = path::join(".", "test.h"); - addFile(path, test); + add_file(path, test); run(); @@ -164,7 +173,7 @@ TEST_F(Directive, Condition) { #$(7)endif )cpp"; - addMain("main.cpp", code); + add_main("main.cpp", code); run("-std=c++23"); EXPECT_EQ(conditions.size(), 8); @@ -198,7 +207,7 @@ int y = $(6)expr($(7)expr(1)); )cpp"; - addMain("main.cpp", code); + add_main("main.cpp", code); run(); EXPECT_EQ(macros.size(), 9); @@ -220,7 +229,7 @@ $(1)#pragma region $(2)#pragma endregion )cpp"; - addMain("main.cpp", code); + add_main("main.cpp", code); run(); EXPECT_EQ(3, pragmas.size()); diff --git a/tests/unit/Compiler/Module.cpp b/tests/unit/Compiler/Module.cpp index 6369fab8..89b267e7 100644 --- a/tests/unit/Compiler/Module.cpp +++ b/tests/unit/Compiler/Module.cpp @@ -13,7 +13,7 @@ PCMInfo buildPCM(llvm::StringRef file, llvm::StringRef code) { std::string path = file.str(); CompilationParams params; - params.outPath = outPath; + params.output_file = outPath; params.arguments = { "clang++", "-std=c++20", diff --git a/tests/unit/Compiler/Preamble.cpp b/tests/unit/Compiler/Preamble.cpp index 81bf3e46..eac33f7c 100644 --- a/tests/unit/Compiler/Preamble.cpp +++ b/tests/unit/Compiler/Preamble.cpp @@ -1,6 +1,7 @@ #include "Test/Test.h" #include "Compiler/Preamble.h" #include "Compiler/Compilation.h" +#include "Test/Annotation.h" namespace clice::testing { @@ -9,13 +10,14 @@ namespace { void EXPECT_BOUNDS(std::vector marks, llvm::StringRef content, LocationChain chain = LocationChain()) { - Annotation annotation{content}; - auto bounds = computePreambleBounds(annotation.source()); + auto annotation = AnnotatedSource::from(content); + + auto bounds = compute_preamble_bounds(annotation.content); ASSERT_EQ(bounds.size(), marks.size(), chain); for(std::uint32_t i = 0; i < bounds.size(); i++) { - EXPECT_EQ(bounds[i], annotation.offset(marks[i]), chain); + EXPECT_EQ(bounds[i], annotation.offsets[marks[i]], chain); } } @@ -75,8 +77,8 @@ void EXPECT_BUILD_PCH(llvm::StringRef main_file, files.erase(main_file); CompilationParams params; - params.outPath = outPath; - auto bound = computePreambleBound(content); + params.output_file = outPath; + auto bound = compute_preamble_bound(content); params.add_remapped_file(main_file, content, bound); params.arguments = { @@ -125,12 +127,12 @@ TEST(Preamble, Bounds) { EXPECT_BOUNDS({"0"}, "#include $(0)"); EXPECT_BOUNDS({"0"}, "#include $(0)\n"); - EXPECT_BOUNDS({"0", "1"}, + EXPECT_BOUNDS({"0", "1", "2", "3"}, R"cpp( -#ifdef TEST -#include $(0) -#define 1 -#endif$(1) +#ifdef TEST$(0) +#include $(1) +#define 1$(2) +#endif$(3) )cpp"); EXPECT_BOUNDS({"0"}, @@ -139,9 +141,9 @@ TEST(Preamble, Bounds) { int x = 1; )cpp"); - EXPECT_BOUNDS({"0"}, R"cpp( -module; -#include $(0) + EXPECT_BOUNDS({"0", "1"}, R"cpp( +module;$(0) +#include $(1) export module test; )cpp"); } @@ -257,7 +259,7 @@ int y = foo(); std::string content = files["main.cpp"]; files.erase("main.cpp"); - auto bounds = computePreambleBounds(content); + auto bounds = compute_preamble_bounds(content); CompilationParams params; params.arguments = {"clang++", "-std=c++20", "main.cpp"}; @@ -270,11 +272,11 @@ int y = foo(); std::string outPath = std::move(*tmp); params.add_remapped_file("main.cpp", content, bound); - if(params.outPath.empty()) { - params.pch = {params.outPath.str().str(), last_bound}; + if(params.output_file.empty()) { + params.pch = {params.output_file.str().str(), last_bound}; } - params.outPath = outPath; + params.output_file = outPath; last_bound = bound; for(auto& [path, content]: files) { diff --git a/tests/unit/Feature/CodeCompletion.cpp b/tests/unit/Feature/CodeCompletion.cpp index f5810e4b..37fa0ff1 100644 --- a/tests/unit/Feature/CodeCompletion.cpp +++ b/tests/unit/Feature/CodeCompletion.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/CodeCompletion.h" namespace clice::testing { @@ -7,11 +7,11 @@ namespace { struct CodeCompletion : TestFixture { auto code_complete(llvm::StringRef code) { - Annotation annotation = {code}; CompilationParams params; + auto annotation = AnnotatedSource::from(code); params.arguments = {"clang++", "-std=c++20", "main.cpp"}; - params.completion = {"main.cpp", annotation.offset("pos")}; - params.add_remapped_file("main.cpp", annotation.source()); + params.completion = {"main.cpp", annotation.offsets["pos"]}; + params.add_remapped_file("main.cpp", annotation.content); config::CodeCompletionOption options = {}; auto result = feature::code_complete(params, options); diff --git a/tests/unit/Feature/DocumentLink.cpp b/tests/unit/Feature/DocumentLink.cpp index 675e3796..2ec46455 100644 --- a/tests/unit/Feature/DocumentLink.cpp +++ b/tests/unit/Feature/DocumentLink.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/DocumentLink.h" namespace clice::testing { @@ -8,20 +8,23 @@ namespace { struct DocumentLink : TestFixture { index::Shared result; + using Self = DocumentLink; + void run(llvm::StringRef code) { - addMain("main.cpp", code); + add_main("main.cpp", code); Tester::compile(); result = feature::indexDocumentLink(*unit); } - void EXPECT_LINK(uint32_t index, + void EXPECT_LINK(this Self& self, + uint32_t index, llvm::StringRef begin, llvm::StringRef end, llvm::StringRef path, LocationChain chain = LocationChain()) { - auto& link = result[unit->interested_file()][index]; - EXPECT_EQ(link.range.begin, offset(begin), chain); - EXPECT_EQ(link.range.end, offset(end), chain); + auto& link = self.result[self.unit->interested_file()][index]; + EXPECT_EQ(link.range.begin, self["main.cpp", begin], chain); + EXPECT_EQ(link.range.end, self["main.cpp", end], chain); EXPECT_EQ(link.file, path, chain); } @@ -60,9 +63,9 @@ TEST_F(DocumentLink, Include) { auto ppragma_once = path::join(".", "pragma_once.h"); auto pguard_macro = path::join(".", "guard_macro.h"); - addFile(ptest, test); - addFile(ppragma_once, pragma_once); - addFile(pguard_macro, guard_macro); + add_file(ptest, test); + add_file(ppragma_once, pragma_once); + add_file(pguard_macro, guard_macro); run(main); auto& links = result[unit->interested_file()]; @@ -87,7 +90,7 @@ TEST_F(DocumentLink, HasInclude) { )cpp"; auto path = path::join(".", "test.h"); - addFile(path, test); + add_file(path, test); run(main); diff --git a/tests/unit/Feature/DocumentSymbol.cpp b/tests/unit/Feature/DocumentSymbol.cpp index e3a4055d..7e451135 100644 --- a/tests/unit/Feature/DocumentSymbol.cpp +++ b/tests/unit/Feature/DocumentSymbol.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/DocumentSymbol.h" namespace clice::testing { @@ -9,7 +9,7 @@ struct DocumentSymbol : TestFixture { protected: auto run(llvm::StringRef code) { - addMain("main.cpp", code); + add_main("main.cpp", code); Tester::compile(); EXPECT_TRUE(unit.has_value()); @@ -216,8 +216,8 @@ int y = 2; )cpp"; Tester tx; - tx.addFile(path::join(".", "header.h"), header); - tx.addMain("main.cpp", main); + tx.add_file(path::join(".", "header.h"), header); + tx.add_main("main.cpp", main); tx.compile(); auto& info = tx.unit; diff --git a/tests/unit/Feature/FoldingRange.cpp b/tests/unit/Feature/FoldingRange.cpp index 924754f0..ed4965ec 100644 --- a/tests/unit/Feature/FoldingRange.cpp +++ b/tests/unit/Feature/FoldingRange.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/FoldingRange.h" namespace clice::testing { @@ -9,21 +9,24 @@ struct FoldingRange : TestFixture { std::vector result; void run(llvm::StringRef source) { - addMain("main.cpp", source); + add_main("main.cpp", source); TestFixture::compile(); result = feature::foldingRanges(*unit); } - void EXPECT_RANGE(std::size_t index, + using Self = FoldingRange; + + void EXPECT_RANGE(this Self& self, + std::size_t index, llvm::StringRef begin, llvm::StringRef end, feature::FoldingRangeKind kind, LocationChain chain = LocationChain()) { - auto& folding = result[index]; - auto begOff = offset(begin); - EXPECT_EQ(begOff, folding.range.begin, chain); - auto endOff = offset(end); - EXPECT_EQ(endOff, folding.range.end, chain); + auto& folding = self.result[index]; + auto begin_offset = self["main.cpp", begin]; + EXPECT_EQ(begin_offset, folding.range.begin, chain); + auto end_offset = self["main.cpp", end]; + EXPECT_EQ(end_offset, folding.range.end, chain); } }; diff --git a/tests/unit/Feature/Hover.cpp b/tests/unit/Feature/Hover.cpp index 76c91427..dc5d5ac1 100644 --- a/tests/unit/Feature/Hover.cpp +++ b/tests/unit/Feature/Hover.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/Hover.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -23,7 +23,7 @@ struct Hover : TestFixture { llvm::StringMap decls; void run(llvm::StringRef code, LocalSourceRange range = {}) { - addMain("main.cpp", code); + add_main("main.cpp", code); compile(); DeclCollector collector; collector.TraverseTranslationUnitDecl(unit->tu()); diff --git a/tests/unit/Feature/InlayHint.cpp b/tests/unit/Feature/InlayHint.cpp index 6498b9be..ed0c1db0 100644 --- a/tests/unit/Feature/InlayHint.cpp +++ b/tests/unit/Feature/InlayHint.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/InlayHint.h" namespace clice::testing { @@ -449,8 +449,8 @@ namespace _2 { )cpp"; Tester tx; - tx.addFile(path::join(".", "header.h"), header); - tx.addMain("main.cpp", source); + tx.add_file(path::join(".", "header.h"), header); + tx.add_main("main.cpp", source); tx.compile(); auto& info = tx.unit; diff --git a/tests/unit/Feature/SemanticToken.cpp b/tests/unit/Feature/SemanticToken.cpp index ab881a85..85120402 100644 --- a/tests/unit/Feature/SemanticToken.cpp +++ b/tests/unit/Feature/SemanticToken.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/SemanticToken.h" namespace clice::testing { @@ -6,23 +6,25 @@ namespace clice::testing { namespace { struct SemanticToken : TestFixture { - index::Shared result; + feature::SemanticTokens tokens; + + using Self = SemanticToken; void run(llvm::StringRef code) { - addMain("main.cpp", code); - Tester::compile(); - result = feature::indexSemanticToken(*unit); + add_main("main.cpp", code); + Tester::compile_with_pch(); + tokens = feature::semantic_tokens(*unit); } - void EXPECT_TOKEN(llvm::StringRef pos, + void EXPECT_TOKEN(this Self& self, + llvm::StringRef pos, SymbolKind kind, uint32_t length, LocationChain chain = LocationChain()) { bool visited = false; - auto offset = offsets[pos]; - auto& tokens = result[unit->interested_file()]; + auto offset = self["main.cpp", pos]; - for(auto& token: tokens) { + for(auto& token: self.tokens) { if(token.range.begin == offset) { EXPECT_EQ(token.kind, kind, chain); EXPECT_EQ(token.range.end - token.range.begin, length, chain); @@ -34,16 +36,16 @@ struct SemanticToken : TestFixture { EXPECT_EQ(visited, true, chain); } - void EXPECT_TOKEN(llvm::StringRef pos, + void EXPECT_TOKEN(this Self& self, + llvm::StringRef pos, SymbolKind kind, SymbolModifiers modifiers, uint32_t length, LocationChain chain = LocationChain()) { bool visited = false; - auto offset = offsets[pos]; - auto& tokens = result[unit->interested_file()]; + auto offset = self["main.cpp", pos]; - for(auto& token: tokens) { + for(auto& token: self.tokens) { if(token.range.begin == offset) { EXPECT_EQ(token.kind, kind, chain); EXPECT_EQ(token.range.end - token.range.begin, length, chain); @@ -56,8 +58,7 @@ struct SemanticToken : TestFixture { EXPECT_EQ(visited, true, chain); } - void dumpResult() { - auto& tokens = result[unit->interested_file()]; + void dump_result() { for(auto& token: tokens) { clice::println("token: {}", dump(token)); } @@ -67,25 +68,23 @@ struct SemanticToken : TestFixture { using enum SymbolKind::Kind; using enum SymbolModifiers::Kind; -/// FIXME: headers not found -/// -/// TEST_F(SemanticToken, Include) { -/// run(R"cpp( -/// $(0)#include $(1) -/// $(2)#include $(3)"stddef.h" -/// $(4)# $(5)include $(6)"stddef.h" -/// )cpp"); -/// -/// /// FIXME: Included file could be macro. -/// -/// EXPECT_TOKEN("0", Directive, 8); -/// EXPECT_TOKEN("1", Header, 10); -/// EXPECT_TOKEN("2", Directive, 8); -/// EXPECT_TOKEN("3", Header, 10); -/// EXPECT_TOKEN("4", Directive, 1); -/// EXPECT_TOKEN("5", Directive, 7); -/// EXPECT_TOKEN("6", Header, 10); -/// } +TEST_F(SemanticToken, Include) { + run(R"cpp( + $(0)#include $(1) + $(2)#include $(3)"stddef.h" + $(4)# $(5)include $(6)"stddef.h" + )cpp"); + + /// FIXME: Included file could be macro. + + EXPECT_TOKEN("0", Directive, 8); + EXPECT_TOKEN("1", Header, 10); + EXPECT_TOKEN("2", Directive, 8); + EXPECT_TOKEN("3", Header, 10); + EXPECT_TOKEN("4", Directive, 1); + EXPECT_TOKEN("5", Directive, 7); + EXPECT_TOKEN("6", Header, 10); +} TEST_F(SemanticToken, Comment) { run(R"cpp( diff --git a/tests/unit/Feature/SignatureHelp.cpp b/tests/unit/Feature/SignatureHelp.cpp index 0a1a59c6..ed2fb533 100644 --- a/tests/unit/Feature/SignatureHelp.cpp +++ b/tests/unit/Feature/SignatureHelp.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Feature/SignatureHelp.h" namespace clice::testing { diff --git a/tests/unit/Index/FeatureIndex.cpp b/tests/unit/Index/FeatureIndex.cpp deleted file mode 100644 index f4e83a6a..00000000 --- a/tests/unit/Index/FeatureIndex.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Test/CTest.h" -#include "Index/FeatureIndex.h" - -namespace clice::testing { - -namespace { - -TEST(FeatureIndex, SemanticTokens) { - llvm::StringRef content = "int x = 1;"; - Tester tester("main.cpp", content); - tester.compile(); - - /// auto index = index(*tester.info); - - // auto& m = index.at(tester.info->interested_file()); - // - // auto tokens = m.semanticTokens(); - // - // EXPECT_EQ(tokens.size(), 3); - // EXPECT_EQ(tokens[0].kind, SymbolKind::Keyword); - // EXPECT_EQ(tokens[1].kind, SymbolKind::Variable); - // EXPECT_EQ(tokens[2].kind, SymbolKind::Number); -} - -} // namespace - -} // namespace clice::testing diff --git a/tests/unit/Index/Function.cpp b/tests/unit/Index/Function.cpp index 18316d71..287f09fb 100644 --- a/tests/unit/Index/Function.cpp +++ b/tests/unit/Index/Function.cpp @@ -1,76 +1,75 @@ -#include "Test/IndexTester.h" - -namespace clice::testing { - -namespace { - -TEST(Index, FunctionParams) { - const char* code = R"cpp( - void foo(int); - - void bar(int, int y = 2); -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); - - /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. - /// add tests for find references ..., !test symbol count. -} - -TEST(Index, FunctionType) { - const char* code = R"cpp( - template - struct function; - - void bar(function& f); - - void bar2(function& f); -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); - /// println("{}", data); - /// tester.info->tu()->dump(); - - /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. - /// add tests for find references ..., !test symbol count. -} - -TEST(Index, Method) { - const char* code = R"cpp( - template - struct function; - - template - requires (__is_same(U, int)) - struct function { - void foo(); - }; - - template - requires (__is_same(U, float)) - struct function { - void foo(); - }; -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); - /// println("{}", data); - /// tester.info->tu()->dump(); - - /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. - /// add tests for find references ..., !test symbol count. -} - -} // namespace - -} // namespace clice::testing - +/// #include "Test/IndexTester.h" +/// +/// namespace clice::testing { +/// +/// namespace { +/// +/// TEST(Index, FunctionParams) { +/// const char* code = R"cpp( +/// void foo(int); +/// +/// void bar(int, int y = 2); +/// )cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); +/// +/// /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. +/// /// add tests for find references ..., !test symbol count. +/// } +/// +/// TEST(Index, FunctionType) { +/// const char* code = R"cpp( +/// template +/// struct function; +/// +/// void bar(function& f); +/// +/// void bar2(function& f); +/// )cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); +/// /// println("{}", data); +/// /// tester.info->tu()->dump(); +/// +/// /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. +/// /// add tests for find references ..., !test symbol count. +/// } +/// +/// TEST(Index, Method) { +/// const char* code = R"cpp( +/// template +/// struct function; +/// +/// template +/// requires (__is_same(U, int)) +/// struct function { +/// void foo(); +/// }; +/// +/// template +/// requires (__is_same(U, float)) +/// struct function { +/// void foo(); +/// }; +/// )cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// /// auto data = tester.indices.find(tester.info->interested_file())->second.toJSON(); +/// /// println("{}", data); +/// /// tester.info->tu()->dump(); +/// +/// /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. +/// /// add tests for find references ..., !test symbol count. +/// } +/// +/// } // namespace +/// +/// } // namespace clice::testing diff --git a/tests/unit/Index/HeaderIndex.cpp b/tests/unit/Index/HeaderIndex.cpp index df72bd53..e37b26ee 100644 --- a/tests/unit/Index/HeaderIndex.cpp +++ b/tests/unit/Index/HeaderIndex.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Index/HeaderIndex.h" namespace clice::testing { diff --git a/tests/unit/Index/SymbolIndex.cpp b/tests/unit/Index/SymbolIndex.cpp deleted file mode 100644 index 01d7c995..00000000 --- a/tests/unit/Index/SymbolIndex.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "Test/CTest.h" -#include "Index/SymbolIndex.h" - -namespace clice::testing { - -// struct SymbolIndex : ::testing::Test, Tester { -// index::Shared> indices; -// index::SymbolIndex index = {nullptr, 0}; -// -// void run(llvm::StringRef code) { -// addMain("main.cpp", code); -// Tester::compile(); -// indices = index::SymbolIndex::build(*AST); -// index = { -// indices[unit->interested_file()].data(), -// static_cast(indices[unit->interested_file()].size()), -// }; -// } -// -// void EXPECT_OCCURENCE(int index, -// llvm::StringRef pos, -// llvm::StringRef name, -// LocationChain chain = LocationChain()) { -// auto occurrence = this->index.occurrences()[index]; -// EXPECT_EQ(occurrence.range().begin, this->offset(pos), chain); -// EXPECT_EQ(occurrence.symbol().name(), name, chain); -// } -// -// index::Symbol EXPECT_SYMBOL(llvm::StringRef name, -// SymbolKind kind, -// LocationChain chain = LocationChain()) { -// for(auto symbol: index.symbols()) { -// if(symbol.name() == name) { -// EXPECT_EQ(symbol.kind(), kind, chain); -// return symbol; -// } -// } -// -// EXPECT_FAILURE("can not find symbol", chain); -// std::unreachable(); -// } -// -// void EXPECT_RELATION(llvm::StringRef name, -// RelationKind kind, -// llvm::StringRef pos, -// LocationChain chain = LocationChain()) { -// bool foundSymbol = false; -// for(auto symbol: index.symbols()) { -// if(symbol.name() == name) { -// foundSymbol = true; -// -// bool foundRelation = false; -// for(auto relation: symbol.relations()) { -// if((relation.kind() & kind) && relation.range().begin == offset(pos)) { -// foundRelation = true; -// break; -// } -// } -// foundRelation = true; -// EXPECT_EQ(foundRelation, true, chain); -// -// break; -// } -// } -// EXPECT_EQ(foundSymbol, true, chain); -// } -// }; - -namespace { - -// TEST_F(SymbolIndex, Symbols) { -// llvm::StringRef code = R"( -// int x = 1; -// int y = 2; -// -// struct Foo {}; -//)"; -// -// run(code); -// -// EXPECT_EQ(index.symbols().length(), 3); -// EXPECT_EQ(index.path(), "main.cpp"); -// EXPECT_EQ(index.content(), code); -// } -// -// TEST_F(SymbolIndex, Occurrences) { -// llvm::StringRef code = R"( -// int @x = 1; -// int @y = 2; -// -// struct @Foo {}; -//)"; -// run(code); -// -// EXPECT_EQ(index.occurrences().length(), 3); -// EXPECT_EQ(index.path(), "main.cpp"); -// -// /// FIXME: Use StringMap to store sources. -// EXPECT_EQ(index.content(), sources[0]); -// -// EXPECT_OCCURENCE(0, "x", "x"); -// EXPECT_OCCURENCE(1, "y", "y"); -// EXPECT_OCCURENCE(2, "Foo", "Foo"); -// } - -// TEST_F(SymbolIndex, Relations) { -// llvm::StringRef code = R"( -// int $(1)x = 1; -// -// int main() { -// $(2)x = 2; -// } -//)"; -// run(code); -// -// EXPECT_SYMBOL("x", SymbolKind::Variable); -// EXPECT_RELATION("x", RelationKind::Definition, "1"); -// EXPECT_RELATION("x", RelationKind::Reference, "2"); -// } - -// TEST_F(SymbolIndex, LocateSymbol) { -// llvm::StringRef code = R"( -// int $(1)x = 1; -// -// int main() { -// $(2)x = 2; -// } -//)"; -// run(code); -// -// auto symbols = index.locateSymbol(offset("1")); -// EXPECT_EQ(symbols.size(), 1); -// EXPECT_EQ(symbols[0].name(), "x"); -// -// symbols = index.locateSymbol(offset("2")); -// EXPECT_EQ(symbols.size(), 1); -// EXPECT_EQ(symbols[0].name(), "x"); -// -// auto symbol = index.locateSymbol(symbols[0].id()); -// EXPECT_EQ(symbol.has_value(), true); -// EXPECT_EQ(symbols[0].name(), "x"); -// } - -// FIXME: headers not found -// -/// TEST_F(SymbolIndex, JSON) { -/// llvm::StringRef code = R"( -/// #include -/// )"; -/// run(code); -/// -/// for(auto& [fid, index]: indices) { -/// if(index.size() == 0) { -/// continue; -/// } -/// -/// index::SymbolIndex sindex(index.data(), index.size()); -/// auto json = sindex.toJSON(); -/// } -/// } - -} // namespace - -} // namespace clice::testing diff --git a/tests/unit/Index/Template.cpp b/tests/unit/Index/Template.cpp index 7c4a034c..9b62887b 100644 --- a/tests/unit/Index/Template.cpp +++ b/tests/unit/Index/Template.cpp @@ -1,167 +1,167 @@ -#include "Test/IndexTester.h" - -namespace clice::testing { - -namespace { - -TEST(Index, ClassTemplate) { - const char* code = R"cpp( - template - struct $(primary_decl)foo; - - /// using type = $(forward_full)foo; - - template - struct $(primary)foo {}; - - template - struct $(partial_spec_decl)foo; - - template - struct $(partial_spec)foo {}; - - template <> - struct $(full_spec_decl)foo; - - template <> - struct $(full_spec)foo {}; - - template struct $(explicit_primary)foo; - - template struct $(explicit_partial)foo; - - $(implicit_primary_1)foo b; - $(implicit_primary_2)foo c; - $(implicit_partial)foo d; - $(implicit_full)foo a; -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - tester.GotoDefinition("primary_decl", "primary"); - tester.GotoDefinition("explicit_primary", "primary"); - tester.GotoDefinition("implicit_primary_1", "primary"); - tester.GotoDefinition("implicit_primary_2", "primary"); - tester.GotoDefinition("partial_spec_decl", "partial_spec"); - tester.GotoDefinition("explicit_partial", "partial_spec"); - tester.GotoDefinition("implicit_partial", "partial_spec"); - /// FIXME: Figure forward template declaration. - /// tester.GotoDefinition("forward_full", "full_spec"); - tester.GotoDefinition("full_spec_decl", "full_spec"); - tester.GotoDefinition("implicit_full", "full_spec"); - - /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. - /// add tests for find references ..., !test symbol count. -} - -TEST(Index, FunctionTemplate) { - /// Function template doesn't have partial specialization. - const char* code = R"cpp( - template void $(primary_decl)foo(); - - template void $(primary)foo() {} - - template <> void $(spec_decl)foo(); - - template <> void $(spec)foo() {} - - template void $(explicit_primary)foo(); - - int main() { - $(implicit_primary)foo(); - $(implicit_spec)foo(); - } -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - tester.GotoDefinition("primary_decl", "primary"); - /// FIXME: - /// tester.GotoDefinition("explicit_primary", "primary"); - tester.GotoDefinition("implicit_primary", "primary"); - - tester.GotoDefinition("spec_decl", "spec"); - tester.GotoDefinition("implicit_spec", "spec"); -} - -TEST(Index, AliasTemplate) { - const char* code = R"cpp( - template - using $(primary)foo = T; - - $(implicit_primary)foo a; -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - tester.GotoDefinition("implicit_primary", "primary"); -} - -TEST(Index, VarTemplate) { - const char* code = R"cpp( - template - extern int $(primary_decl)foo; - - template - int $(primary)foo = 1; - - template - extern int $(partial_spec_decl)foo; - - template - int $(partial_spec)foo = 2; - - template <> - float $(full_spec)foo = 1.0f; - - template int $(explicit_primary)foo; - - template int $(explicit_partial)foo; - - int main() { - $(implicit_primary_1)foo = 1; - $(implicit_primary_2)foo = 2; - $(implicit_partial)foo = 3; - $(implicit_full)foo = 4; - return 0; - } -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - tester.GotoDefinition("primary_decl", "primary"); - /// tester.GotoDefinition("explicit_primary", "primary"); - tester.GotoDefinition("implicit_primary_1", "primary"); - tester.GotoDefinition("implicit_primary_2", "primary"); - - tester.GotoDefinition("partial_spec_decl", "partial_spec"); - /// tester.GotoDefinition("explicit_partial", "partial_spec"); - tester.GotoDefinition("implicit_partial", "partial_spec"); - - tester.GotoDefinition("implicit_full", "full_spec"); -} - -TEST(Index, Concept) { - const char* code = R"cpp( - template - concept $(primary)foo = true; - - static_assert($(implicit)foo); - - $(implicit2)foo auto bar = 1; -)cpp"; - - IndexTester tester("main.cpp", code); - tester.run(); - - tester.GotoDefinition("primary", "primary"); - tester.GotoDefinition("implicit", "primary"); - tester.GotoDefinition("implicit2", "primary"); -} - -} // namespace - -} // namespace clice::testing +/// #include "Test/IndexTester.h" +/// +/// namespace clice::testing { +/// +/// namespace { +/// +/// TEST(Index, ClassTemplate) { +/// const char* code = R"cpp( +/// template +/// struct $(primary_decl)foo; +/// +/// /// using type = $(forward_full)foo; +/// +/// template +/// struct $(primary)foo {}; +/// +/// template +/// struct $(partial_spec_decl)foo; +/// +/// template +/// struct $(partial_spec)foo {}; +/// +/// template <> +/// struct $(full_spec_decl)foo; +/// +/// template <> +/// struct $(full_spec)foo {}; +/// +/// template struct $(explicit_primary)foo; +/// +/// template struct $(explicit_partial)foo; +/// +/// $(implicit_primary_1)foo b; +/// $(implicit_primary_2)foo c; +/// $(implicit_partial)foo d; +/// $(implicit_full)foo a; +///)cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// tester.GotoDefinition("primary_decl", "primary"); +/// tester.GotoDefinition("explicit_primary", "primary"); +/// tester.GotoDefinition("implicit_primary_1", "primary"); +/// tester.GotoDefinition("implicit_primary_2", "primary"); +/// tester.GotoDefinition("partial_spec_decl", "partial_spec"); +/// tester.GotoDefinition("explicit_partial", "partial_spec"); +/// tester.GotoDefinition("implicit_partial", "partial_spec"); +/// /// FIXME: Figure forward template declaration. +/// /// tester.GotoDefinition("forward_full", "full_spec"); +/// tester.GotoDefinition("full_spec_decl", "full_spec"); +/// tester.GotoDefinition("implicit_full", "full_spec"); +/// +/// /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. +/// /// add tests for find references ..., !test symbol count. +/// } +/// +/// TEST(Index, FunctionTemplate) { +/// /// Function template doesn't have partial specialization. +/// const char* code = R"cpp( +/// template void $(primary_decl)foo(); +/// +/// template void $(primary)foo() {} +/// +/// template <> void $(spec_decl)foo(); +/// +/// template <> void $(spec)foo() {} +/// +/// template void $(explicit_primary)foo(); +/// +/// int main() { +/// $(implicit_primary)foo(); +/// $(implicit_spec)foo(); +/// } +///)cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// tester.GotoDefinition("primary_decl", "primary"); +/// /// FIXME: +/// /// tester.GotoDefinition("explicit_primary", "primary"); +/// tester.GotoDefinition("implicit_primary", "primary"); +/// +/// tester.GotoDefinition("spec_decl", "spec"); +/// tester.GotoDefinition("implicit_spec", "spec"); +/// } +/// +/// TEST(Index, AliasTemplate) { +/// const char* code = R"cpp( +/// template +/// using $(primary)foo = T; +/// +/// $(implicit_primary)foo a; +///)cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// tester.GotoDefinition("implicit_primary", "primary"); +/// } +/// +/// TEST(Index, VarTemplate) { +/// const char* code = R"cpp( +/// template +/// extern int $(primary_decl)foo; +/// +/// template +/// int $(primary)foo = 1; +/// +/// template +/// extern int $(partial_spec_decl)foo; +/// +/// template +/// int $(partial_spec)foo = 2; +/// +/// template <> +/// float $(full_spec)foo = 1.0f; +/// +/// template int $(explicit_primary)foo; +/// +/// template int $(explicit_partial)foo; +/// +/// int main() { +/// $(implicit_primary_1)foo = 1; +/// $(implicit_primary_2)foo = 2; +/// $(implicit_partial)foo = 3; +/// $(implicit_full)foo = 4; +/// return 0; +/// } +///)cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// tester.GotoDefinition("primary_decl", "primary"); +/// /// tester.GotoDefinition("explicit_primary", "primary"); +/// tester.GotoDefinition("implicit_primary_1", "primary"); +/// tester.GotoDefinition("implicit_primary_2", "primary"); +/// +/// tester.GotoDefinition("partial_spec_decl", "partial_spec"); +/// /// tester.GotoDefinition("explicit_partial", "partial_spec"); +/// tester.GotoDefinition("implicit_partial", "partial_spec"); +/// +/// tester.GotoDefinition("implicit_full", "full_spec"); +/// } +/// +/// TEST(Index, Concept) { +/// const char* code = R"cpp( +/// template +/// concept $(primary)foo = true; +/// +/// static_assert($(implicit)foo); +/// +/// $(implicit2)foo auto bar = 1; +///)cpp"; +/// +/// IndexTester tester("main.cpp", code); +/// tester.run(); +/// +/// tester.GotoDefinition("primary", "primary"); +/// tester.GotoDefinition("implicit", "primary"); +/// tester.GotoDefinition("implicit2", "primary"); +/// } +/// +/// } // namespace +/// +/// } // namespace clice::testing diff --git a/tests/unit/Index/USR.cpp b/tests/unit/Index/USR.cpp index f0a58d6d..295ff452 100644 --- a/tests/unit/Index/USR.cpp +++ b/tests/unit/Index/USR.cpp @@ -1,5 +1,5 @@ #include "Support/Logger.h" -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Index/USR.h" #include "clang/AST/DeclBase.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -45,10 +45,11 @@ struct GetUSRVisitor : public clang::RecursiveASTVisitor { std::map USRs; }; -class USRTester : public Tester { - using Tester::Tester; +struct USRTester : public Tester { + USRTester(llvm::StringRef file, llvm::StringRef content) { + add_main(file, content); + } -public: void run(llvm::StringRef standard = "-std=c++20") { Tester::compile(standard); @@ -58,7 +59,7 @@ public: } llvm::StringRef lookupUSR(llvm::StringRef key) { - auto iter = USRs.find(offset(key)); + auto iter = USRs.find((*this)["main.cpp", key]); if(iter == USRs.end()) { log::fatal("USR not found for key: {}", key); } @@ -74,24 +75,24 @@ public: /// llvm::StringRef content = R"cpp( /// #include /// template struct A; -/// +/// /// template requires (__is_same(T, float)) /// struct $(1)A; -/// +/// /// template requires (__is_same(T, int)) /// struct $(2)A; -/// +/// /// template requires (std::same_as) /// struct $(3)A {}; /// )cpp"; -/// +/// /// USRTester tester("main.cpp", content); /// tester.run(); -/// +/// /// auto usr1 = tester.lookupUSR("1"); /// auto usr2 = tester.lookupUSR("2"); /// auto usr3 = tester.lookupUSR("3"); -/// +/// /// EXPECT_NE(usr1, usr2); /// EXPECT_NE(usr1, usr3); /// EXPECT_NE(usr2, usr3); diff --git a/tests/unit/Support/Binary.cpp b/tests/unit/Support/Binary.cpp index 4ee93d89..358048cb 100644 --- a/tests/unit/Support/Binary.cpp +++ b/tests/unit/Support/Binary.cpp @@ -1,4 +1,4 @@ -#include "Test/CTest.h" +#include "Test/Tester.h" #include "Support/Binary.h" namespace clice::testing {