From ab1dcc00bc28521fd3bc756b2a0fad8b79d35427 Mon Sep 17 00:00:00 2001 From: csh Date: Sun, 14 Dec 2025 11:10:50 +0800 Subject: [PATCH] :bug: fix(uses): make file-level uses globally visible - Preserve top-level `uses` imports and apply them to subsequent unit/class/function/method scopes. - Extend semantic ResolveFromUses to consult file-level imports (and fall back from unit imports). --- .../src/language/semantic/analyzer.cppm | 133 ++++++++++-------- .../src/language/semantic/interface.cppm | 1 + .../src/language/symbol/internal/builder.cppm | 30 ++++ lsp-server/src/language/symbol/types.cppm | 3 + 4 files changed, 111 insertions(+), 56 deletions(-) diff --git a/lsp-server/src/language/semantic/analyzer.cppm b/lsp-server/src/language/semantic/analyzer.cppm index a291275..6121071 100644 --- a/lsp-server/src/language/semantic/analyzer.cppm +++ b/lsp-server/src/language/semantic/analyzer.cppm @@ -518,21 +518,28 @@ namespace lsp::language::semantic void Analyzer::VisitUsesStatement(ast::UsesStatement& node) { + auto add_to = [&](std::vector& target) { + target.reserve(target.size() + node.units.size()); + for (const auto& unit : node.units) + { + symbol::UnitImport import; + import.unit_name = unit.name; + import.location = unit.location; + target.push_back(std::move(import)); + } + }; + if (!current_unit_context_) { + // Top-level `uses` is treated as file-global. + add_to(file_imports_); return; } auto& context = *current_unit_context_; auto* target = current_unit_section_ == symbol::UnitVisibility::kInterface ? &context.interface_imports : &context.implementation_imports; - for (const auto& unit : node.units) - { - symbol::UnitImport import; - import.unit_name = unit.name; - import.location = unit.location; - target->push_back(std::move(import)); - } + add_to(*target); } void Analyzer::VisitIdentifier(ast::Identifier& node) @@ -1167,64 +1174,78 @@ namespace lsp::language::semantic std::optional Analyzer::ResolveFromUses(const std::string& name) { - if (!current_unit_context_) - { - return std::nullopt; - } - - // 获取当前上下文的导入列表 - const auto& imports = current_unit_section_ == symbol::UnitVisibility::kInterface ? current_unit_context_->interface_imports : current_unit_context_->implementation_imports; - - // 从后往前查找(最后导入的优先级最高) - for (auto it = imports.rbegin(); it != imports.rend(); ++it) - { - const auto& unit_import = *it; - - // 1. 查找导入的 unit 符号 - auto unit_matches = symbol_table_.FindSymbolsByName(unit_import.unit_name); - if (unit_matches.empty()) + auto resolve_from_imports = [&](const std::vector& imports) -> std::optional { + // 从后往前查找(最后导入的优先级最高) + for (auto it = imports.rbegin(); it != imports.rend(); ++it) { - // 如果本地找不到,尝试通过 external_symbol_provider_ 查找 - if (external_symbol_provider_) + const auto& unit_import = *it; + + // 1. 查找导入的 unit 符号 + auto unit_matches = symbol_table_.FindSymbolsByName(unit_import.unit_name); + if (unit_matches.empty()) { - auto external_unit = external_symbol_provider_(unit_import.unit_name); - if (external_unit) + // 如果本地找不到,尝试通过 external_symbol_provider_ 查找 + if (external_symbol_provider_) { - auto unit_id = symbol_table_.CreateSymbol(std::move(*external_unit)); - unit_matches.push_back(unit_id); + auto external_unit = external_symbol_provider_(unit_import.unit_name); + if (external_unit) + { + auto unit_id = symbol_table_.CreateSymbol(std::move(*external_unit)); + unit_matches.push_back(unit_id); + } + } + } + + // 2. 在每个找到的 unit 中查找目标符号 + for (auto unit_id : unit_matches) + { + const auto* unit_symbol = symbol_table_.definition(unit_id); + if (!unit_symbol || !unit_symbol->Is()) + { + continue; + } + + // 3. 查找 unit 的作用域 + auto unit_scope_id = FindScopeOwnedBy(unit_id); + if (!unit_scope_id) + { + continue; + } + + // 4. 在 unit 的 Interface 作用域中查找符号 + auto symbol_id = symbol_table_.scopes().FindSymbolInScope(*unit_scope_id, name); + if (symbol_id) + { + // 验证符号是否在 Interface 部分(通过 visibility 检查) + const auto* symbol = symbol_table_.definition(*symbol_id); + if (symbol) + { + // 如果符号有 visibility 信息,确保它是 Interface 可见的 + // TODO: 需要符号表支持 visibility 标记 + return symbol_id; + } } } } - // 2. 在每个找到的 unit 中查找目标符号 - for (auto unit_id : unit_matches) + return std::nullopt; + }; + + if (current_unit_context_) + { + // 获取当前上下文的导入列表 + const auto& imports = current_unit_section_ == symbol::UnitVisibility::kInterface ? current_unit_context_->interface_imports : current_unit_context_->implementation_imports; + if (auto result = resolve_from_imports(imports)) { - const auto* unit_symbol = symbol_table_.definition(unit_id); - if (!unit_symbol || !unit_symbol->Is()) - { - continue; - } + return result; + } + } - // 3. 查找 unit 的作用域 - auto unit_scope_id = FindScopeOwnedBy(unit_id); - if (!unit_scope_id) - { - continue; - } - - // 4. 在 unit 的 Interface 作用域中查找符号 - auto symbol_id = symbol_table_.scopes().FindSymbolInScope(*unit_scope_id, name); - if (symbol_id) - { - // 验证符号是否在 Interface 部分(通过 visibility 检查) - const auto* symbol = symbol_table_.definition(*symbol_id); - if (symbol) - { - // 如果符号有 visibility 信息,确保它是 Interface 可见的 - // TODO: 需要符号表支持 visibility 标记 - return symbol_id; - } - } + if (!file_imports_.empty()) + { + if (auto result = resolve_from_imports(file_imports_)) + { + return result; } } diff --git a/lsp-server/src/language/semantic/interface.cppm b/lsp-server/src/language/semantic/interface.cppm index 368c43e..79e528e 100644 --- a/lsp-server/src/language/semantic/interface.cppm +++ b/lsp-server/src/language/semantic/interface.cppm @@ -681,6 +681,7 @@ export namespace lsp::language::semantic std::optional current_class_id_; std::optional current_unit_context_; std::optional current_unit_section_; + std::vector file_imports_; ExternalSymbolProvider external_symbol_provider_; std::unordered_map imported_symbols_; }; diff --git a/lsp-server/src/language/symbol/internal/builder.cppm b/lsp-server/src/language/symbol/internal/builder.cppm index 58f5abc..52e97d4 100644 --- a/lsp-server/src/language/symbol/internal/builder.cppm +++ b/lsp-server/src/language/symbol/internal/builder.cppm @@ -424,6 +424,30 @@ namespace lsp::language::symbol Builder::ScopeGuard Builder::EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range) { + if (!file_imports_.empty() && file_imports_applied_.insert(symbol_id).second) + { + Symbol* owner_symbol = const_cast(table_.definition(symbol_id)); + if (owner_symbol) + { + if (auto* fn = owner_symbol->As()) + { + fn->imports.insert(fn->imports.begin(), file_imports_.begin(), file_imports_.end()); + } + else if (auto* method = owner_symbol->As()) + { + method->imports.insert(method->imports.begin(), file_imports_.begin(), file_imports_.end()); + } + else if (auto* cls = owner_symbol->As()) + { + cls->imports.insert(cls->imports.begin(), file_imports_.begin(), file_imports_.end()); + } + else if (auto* unit = owner_symbol->As()) + { + unit->interface_imports.insert(unit->interface_imports.begin(), file_imports_.begin(), file_imports_.end()); + unit->implementation_imports.insert(unit->implementation_imports.begin(), file_imports_.begin(), file_imports_.end()); + } + } + } current_scope_id_ = table_.CreateScope(kind, range, current_scope_id_, symbol_id); return ScopeGuard(*this, current_scope_id_); } @@ -798,6 +822,12 @@ namespace lsp::language::symbol const auto* scope_info = table_.scopes().scope(current_scope_id_); if (!scope_info || !scope_info->owner) { + // Top-level `uses` (no owner scope) is treated as file-global. + file_imports_.reserve(file_imports_.size() + node.units.size()); + for (const auto& unit : node.units) + { + file_imports_.push_back({ unit.name, unit.location }); + } return; } diff --git a/lsp-server/src/language/symbol/types.cppm b/lsp-server/src/language/symbol/types.cppm index 3010a53..a328e56 100644 --- a/lsp-server/src/language/symbol/types.cppm +++ b/lsp-server/src/language/symbol/types.cppm @@ -608,6 +608,9 @@ export namespace lsp::language::symbol std::optional current_parent_symbol_id_; std::optional current_function_id_; + std::vector file_imports_; + std::unordered_set file_imports_applied_; + bool in_interface_section_; ast::AccessModifier current_access_modifier_ = ast::AccessModifier::kPublic; };