From 8743e1f948a919be4b0bfcc6ea206d63fe8679c7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sat, 16 Nov 2024 09:50:28 +0100 Subject: [PATCH 1/2] feat: Add APIs to stop parsing Signed-off-by: Roberto Raggi --- src/lsp/cxx/lsp/cxx_document.cc | 4 +++- src/lsp/cxx/lsp/cxx_document.h | 3 ++- src/parser/cxx/parser.cc | 14 ++++++++++++ src/parser/cxx/parser.h | 29 ++++++++----------------- src/parser/cxx/parser_fwd.h | 3 +++ src/parser/cxx/translation_unit.cc | 29 +++++++++++++++++++++++-- src/parser/cxx/translation_unit.h | 34 ++++++++---------------------- 7 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/lsp/cxx/lsp/cxx_document.cc b/src/lsp/cxx/lsp/cxx_document.cc index 10c8878e..d629e15d 100644 --- a/src/lsp/cxx/lsp/cxx_document.cc +++ b/src/lsp/cxx/lsp/cxx_document.cc @@ -172,7 +172,8 @@ void CxxDocument::Private::configure() { CxxDocument::CxxDocument(const CLI& cli, long version) : d(std::make_unique(cli, version)) {} -void CxxDocument::parse(std::string source, std::string fileName) { +void CxxDocument::parse(std::string source, std::string fileName, + std::function stopParsingPredicate) { d->configure(); auto& unit = d->unit; @@ -188,6 +189,7 @@ void CxxDocument::parse(std::string source, std::string fileName) { .staticAssert = cli.opt_fstatic_assert || cli.opt_fcheck, .reflect = !cli.opt_fno_reflect, .templates = cli.opt_ftemplates, + .stopParsingPredicate = std::move(stopParsingPredicate), }); } diff --git a/src/lsp/cxx/lsp/cxx_document.h b/src/lsp/cxx/lsp/cxx_document.h index dff31299..762a3220 100644 --- a/src/lsp/cxx/lsp/cxx_document.h +++ b/src/lsp/cxx/lsp/cxx_document.h @@ -34,7 +34,8 @@ class CxxDocument { explicit CxxDocument(const CLI& cli, long version); ~CxxDocument(); - void parse(std::string source, std::string fileName); + void parse(std::string source, std::string fileName, + std::function stopParsingPredicate = {}); [[nodiscard]] auto version() const -> long; [[nodiscard]] auto diagnostics() const -> Vector; diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 51d19580..397777c1 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -869,6 +869,12 @@ auto Parser::expect(TokenKind tk, SourceLocation& location) -> bool { void Parser::operator()(UnitAST*& ast) { parse(ast); } +auto Parser::config() const -> const ParserConfiguration& { return config_; } + +void Parser::setConfig(ParserConfiguration config) { + config_ = std::move(config); +} + void Parser::parse(UnitAST*& ast) { parse_translation_unit(ast); } void Parser::parse_warn(std::string message) { @@ -1211,6 +1217,8 @@ void Parser::parse_top_level_declaration_seq(UnitAST*& yyast) { LoopParser loop(this); while (LA()) { + if (shouldStopParsing()) break; + loop.start(); DeclarationAST* declaration = nullptr; @@ -1233,6 +1241,8 @@ void Parser::parse_declaration_seq(List*& yyast) { LoopParser loop(this); while (LA()) { + if (shouldStopParsing()) break; + if (lookat(TokenKind::T_RBRACE)) break; if (parse_maybe_module()) break; @@ -8111,6 +8121,8 @@ void Parser::parse_namespace_body(NamespaceDefinitionAST* yyast) { LoopParser loop{this}; while (LA()) { + if (shouldStopParsing()) break; + if (lookat(TokenKind::T_RBRACE)) break; loop.start(); @@ -9160,6 +9172,8 @@ void Parser::parse_class_body(List*& yyast) { LoopParser loop{this}; while (LA()) { + if (shouldStopParsing()) break; + if (lookat(TokenKind::T_RBRACE)) break; loop.start(); diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index ed965bc2..b007955b 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -54,26 +55,6 @@ class Parser final { [[nodiscard]] auto control() const -> Control* { return control_; } - [[nodiscard]] auto config() const -> const ParserConfiguration& { - return config_; - } - - void setConfig(const ParserConfiguration& config) { config_ = config; } - - /** - * Whether to enable fuzzy template resolution. - */ - [[nodiscard]] auto fuzzyTemplateResolution() const -> bool; - - /** - * Sets whether to enable fuzzy template resolution. - * - * When enabled, the parser will try to resolve template names - * - * @param fuzzyTemplateResolution whether to enable fuzzy template resolution - */ - void setFuzzyTemplateResolution(bool fuzzyTemplateResolution); - /** * Parse the given unit. */ @@ -84,6 +65,9 @@ class Parser final { */ void operator()(UnitAST*& ast); + [[nodiscard]] auto config() const -> const ParserConfiguration&; + void setConfig(ParserConfiguration config); + private: struct DeclSpecs; struct Decl; @@ -123,6 +107,11 @@ class Parser final { static auto prec(TokenKind tk) -> Prec; + [[nodiscard]] auto shouldStopParsing() const -> bool { + if (config_.stopParsingPredicate) return config_.stopParsingPredicate(); + return false; + } + [[nodiscard]] auto context_allows_function_definition( BindingContext ctx) const -> bool { if (ctx == BindingContext::kBlock) return false; diff --git a/src/parser/cxx/parser_fwd.h b/src/parser/cxx/parser_fwd.h index 29fbe3af..dfb00efb 100644 --- a/src/parser/cxx/parser_fwd.h +++ b/src/parser/cxx/parser_fwd.h @@ -21,6 +21,8 @@ #pragma once +#include + namespace cxx { class Parser; @@ -31,6 +33,7 @@ struct ParserConfiguration { bool staticAssert = false; bool reflect = true; bool templates = false; + std::function stopParsingPredicate; }; } // namespace cxx diff --git a/src/parser/cxx/translation_unit.cc b/src/parser/cxx/translation_unit.cc index 946cbf48..3423dd3e 100644 --- a/src/parser/cxx/translation_unit.cc +++ b/src/parser/cxx/translation_unit.cc @@ -96,6 +96,27 @@ void TranslationUnit::endPreprocessing() { preprocessor_->squeeze(); } +auto TranslationUnit::fatalErrors() const -> bool { + return diagnosticsClient_->fatalErrors(); +} + +void TranslationUnit::setFatalErrors(bool fatalErrors) { + diagnosticsClient_->setFatalErrors(fatalErrors); +} + +auto TranslationUnit::blockErrors(bool blockErrors) -> bool { + return diagnosticsClient_->blockErrors(blockErrors); +} + +void TranslationUnit::error(SourceLocation loc, std::string message) const { + diagnosticsClient_->report(tokenAt(loc), Severity::Error, std::move(message)); +} + +void TranslationUnit::warning(SourceLocation loc, std::string message) const { + TranslationUnit::diagnosticsClient_->report(tokenAt(loc), Severity::Warning, + std::move(message)); +} + auto TranslationUnit::tokenLength(SourceLocation loc) const -> int { const auto& tk = tokenAt(loc); if (tk.kind() == TokenKind::T_IDENTIFIER) { @@ -143,13 +164,13 @@ auto TranslationUnit::tokenEndPosition(SourceLocation loc) const return preprocessor_->tokenEndPosition(tokenAt(loc)); } -void TranslationUnit::parse(const ParserConfiguration& config) { +void TranslationUnit::parse(ParserConfiguration config) { if (ast_) { cxx_runtime_error("translation unit already parsed"); } Parser parse(this); - parse.setConfig(config); + parse.setConfig(std::move(config)); parse(ast_); } @@ -158,6 +179,10 @@ auto TranslationUnit::globalScope() const -> Scope* { return globalNamespace_->scope(); } +auto TranslationUnit::fileName() const -> const std::string& { + return fileName_; +} + auto TranslationUnit::load(std::span data) -> bool { #ifndef CXX_NO_FLATBUFFERS ASTDecoder decode{this}; diff --git a/src/parser/cxx/translation_unit.h b/src/parser/cxx/translation_unit.h index 3e6c47d9..d758f487 100644 --- a/src/parser/cxx/translation_unit.h +++ b/src/parser/cxx/translation_unit.h @@ -60,14 +60,14 @@ class TranslationUnit { [[nodiscard]] auto globalScope() const -> Scope*; - [[nodiscard]] auto fileName() const -> const std::string& { - return fileName_; - } + [[nodiscard]] auto fileName() const -> const std::string&; [[nodiscard]] auto preprocessor() const -> Preprocessor* { return preprocessor_.get(); } + void parse(ParserConfiguration config = {}); + // set source and preprocess, deprecated. void setSource(std::string source, std::string fileName); @@ -77,27 +77,13 @@ class TranslationUnit { void endPreprocessing(); - [[nodiscard]] auto fatalErrors() const -> bool { - return diagnosticsClient_->fatalErrors(); - } - - void setFatalErrors(bool fatalErrors) { - diagnosticsClient_->setFatalErrors(fatalErrors); - } - - auto blockErrors(bool blockErrors = true) -> bool { - return diagnosticsClient_->blockErrors(blockErrors); - } + [[nodiscard]] auto fatalErrors() const -> bool; + void setFatalErrors(bool fatalErrors); - void error(SourceLocation loc, std::string message) const { - diagnosticsClient_->report(tokenAt(loc), Severity::Error, - std::move(message)); - } + auto blockErrors(bool blockErrors = true) -> bool; - void warning(SourceLocation loc, std::string message) const { - diagnosticsClient_->report(tokenAt(loc), Severity::Warning, - std::move(message)); - } + void error(SourceLocation loc, std::string message) const; + void warning(SourceLocation loc, std::string message) const; // tokens [[nodiscard]] inline auto tokenCount() const -> unsigned { @@ -134,8 +120,6 @@ class TranslationUnit { [[nodiscard]] auto literal(SourceLocation loc) const -> const Literal*; - void parse(const ParserConfiguration& config = {}); - [[nodiscard]] auto load(std::span data) -> bool; [[nodiscard]] auto serialize(std::ostream& out) -> bool; @@ -146,13 +130,13 @@ class TranslationUnit { private: std::unique_ptr control_; std::unique_ptr arena_; + std::unique_ptr preprocessor_; std::vector tokens_; std::string fileName_; UnitAST* ast_ = nullptr; const char* yyptr = nullptr; DiagnosticsClient* diagnosticsClient_ = nullptr; NamespaceSymbol* globalNamespace_ = nullptr; - std::unique_ptr preprocessor_; }; } // namespace cxx From cd7c81f18e7ead4e52afc4c4d9d607607d6865d2 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sat, 16 Nov 2024 10:45:29 +0100 Subject: [PATCH 2/2] Implement cancellation for pending parser requests in the LSP server Signed-off-by: Roberto Raggi --- src/lsp/cxx/lsp/cxx_document.cc | 54 ++++++++++++++++++++++++++++----- src/lsp/cxx/lsp/cxx_document.h | 10 ++++-- src/lsp/cxx/lsp/lsp_server.cc | 32 +++++++++++++++++-- src/lsp/cxx/lsp/lsp_server.h | 3 ++ 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/lsp/cxx/lsp/cxx_document.cc b/src/lsp/cxx/lsp/cxx_document.cc index d629e15d..b8626a67 100644 --- a/src/lsp/cxx/lsp/cxx_document.cc +++ b/src/lsp/cxx/lsp/cxx_document.cc @@ -34,6 +34,10 @@ #include #include +#ifndef CXX_NO_THREADS +#include +#endif + namespace cxx::lsp { namespace { @@ -65,12 +69,20 @@ struct Diagnostics final : cxx::DiagnosticsClient { struct CxxDocument::Private { const CLI& cli; + std::string fileName; long version; Diagnostics diagnosticsClient; TranslationUnit unit{&diagnosticsClient}; std::shared_ptr toolchain; - Private(const CLI& cli, long version) : cli(cli), version(version) {} +#ifndef CXX_NO_THREADS + std::atomic cancelled{false}; +#else + bool cancelled{false}; +#endif + + Private(const CLI& cli, std::string fileName, long version) + : cli(cli), fileName(std::move(fileName)), version(version) {} void configure(); }; @@ -169,27 +181,53 @@ void CxxDocument::Private::configure() { } } -CxxDocument::CxxDocument(const CLI& cli, long version) - : d(std::make_unique(cli, version)) {} +CxxDocument::CxxDocument(const CLI& cli, std::string fileName, long version) + : d(std::make_unique(cli, std::move(fileName), version)) {} -void CxxDocument::parse(std::string source, std::string fileName, - std::function stopParsingPredicate) { +auto CxxDocument::isCancelled() const -> bool { +#ifndef CXX_NO_THREADS + return d->cancelled.load(); +#else + return d->cancelled; +#endif +} + +void CxxDocument::cancel() { +#ifndef CXX_NO_THREADS + d->cancelled.store(true); +#else + d->cancelled = true; +#endif +} + +auto CxxDocument::fileName() const -> const std::string& { return d->fileName; } + +void CxxDocument::parse(std::string source) { d->configure(); auto& unit = d->unit; auto& cli = d->cli; - unit.setSource(std::move(source), fileName); - auto preprocessor = unit.preprocessor(); + DefaultPreprocessorState state{*preprocessor}; + + unit.beginPreprocessing(std::move(source), d->fileName); + + while (state) { + if (isCancelled()) break; + std::visit(state, unit.continuePreprocessing()); + } + + unit.endPreprocessing(); + unit.parse(ParserConfiguration{ .checkTypes = cli.opt_fcheck, .fuzzyTemplateResolution = true, .staticAssert = cli.opt_fstatic_assert || cli.opt_fcheck, .reflect = !cli.opt_fno_reflect, .templates = cli.opt_ftemplates, - .stopParsingPredicate = std::move(stopParsingPredicate), + .stopParsingPredicate = [this] { return isCancelled(); }, }); } diff --git a/src/lsp/cxx/lsp/cxx_document.h b/src/lsp/cxx/lsp/cxx_document.h index 762a3220..c245c44b 100644 --- a/src/lsp/cxx/lsp/cxx_document.h +++ b/src/lsp/cxx/lsp/cxx_document.h @@ -31,11 +31,15 @@ namespace cxx::lsp { class CxxDocument { public: - explicit CxxDocument(const CLI& cli, long version); + explicit CxxDocument(const CLI& cli, std::string fileName, long version); ~CxxDocument(); - void parse(std::string source, std::string fileName, - std::function stopParsingPredicate = {}); + [[nodiscard]] auto isCancelled() const -> bool; + void cancel(); + + [[nodiscard]] auto fileName() const -> const std::string&; + + void parse(std::string source); [[nodiscard]] auto version() const -> long; [[nodiscard]] auto diagnostics() const -> Vector; diff --git a/src/lsp/cxx/lsp/lsp_server.cc b/src/lsp/cxx/lsp/lsp_server.cc index f5f65fae..571bbb3a 100644 --- a/src/lsp/cxx/lsp/lsp_server.cc +++ b/src/lsp/cxx/lsp/lsp_server.cc @@ -286,6 +286,23 @@ void Server::run(std::function task) { task(); } +void Server::cancelPendingParserRequests(const std::string& fileName) { +#ifndef CXX_NO_THREADS + auto lock = std::unique_lock(documentsMutex_); +#endif + + std::vector> pendingParserRequests; + std::swap(pendingParserRequests_, pendingParserRequests); + + for (const auto& doc : pendingParserRequests) { + if (doc->fileName() == fileName) { + doc->cancel(); + } else { + pendingParserRequests_.push_back(doc); + } + } +} + void Server::parse(const std::string& uri) { const auto& doc = documentContents_.at(uri); @@ -293,14 +310,25 @@ void Server::parse(const std::string& uri) { auto version = doc.version; run([text = std::move(text), uri = std::move(uri), version, this] { - auto doc = std::make_shared(cli, version); - doc->parse(std::move(text), pathFromUri(uri)); + auto fileName = pathFromUri(uri); + + cancelPendingParserRequests(fileName); + + auto doc = std::make_shared(cli, std::move(fileName), version); + pendingParserRequests_.push_back(doc); + + doc->parse(std::move(text)); { #ifndef CXX_NO_THREADS auto locker = std::unique_lock(outputMutex_); #endif + if (auto it = std::ranges::find(pendingParserRequests_, doc); + it != pendingParserRequests_.end()) { + pendingParserRequests_.erase(it); + } + if (documents_.contains(uri) && documents_.at(uri)->version() > version) { return; } diff --git a/src/lsp/cxx/lsp/lsp_server.h b/src/lsp/cxx/lsp/lsp_server.h index d5d91107..bdd96f86 100644 --- a/src/lsp/cxx/lsp/lsp_server.h +++ b/src/lsp/cxx/lsp/lsp_server.h @@ -68,6 +68,8 @@ class Server { void startWorkersIfNeeded(); void stopWorkersIfNeeded(); + void cancelPendingParserRequests(const std::string& fileName); + void run(std::function task); void parse(const std::string& uri); @@ -105,6 +107,7 @@ class Server { std::ostream& log; std::unordered_map> documents_; std::unordered_map documentContents_; + std::vector> pendingParserRequests_; #ifndef CXX_NO_THREADS SyncQueue syncQueue_; std::vector workers_;