🐛 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).
This commit is contained in:
csh 2025-12-14 11:10:50 +08:00
parent 171d5e2064
commit ab1dcc00bc
4 changed files with 111 additions and 56 deletions

View File

@ -518,21 +518,28 @@ namespace lsp::language::semantic
void Analyzer::VisitUsesStatement(ast::UsesStatement& node) void Analyzer::VisitUsesStatement(ast::UsesStatement& node)
{ {
auto add_to = [&](std::vector<symbol::UnitImport>& 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_) if (!current_unit_context_)
{ {
// Top-level `uses` is treated as file-global.
add_to(file_imports_);
return; return;
} }
auto& context = *current_unit_context_; auto& context = *current_unit_context_;
auto* target = current_unit_section_ == symbol::UnitVisibility::kInterface ? &context.interface_imports : &context.implementation_imports; auto* target = current_unit_section_ == symbol::UnitVisibility::kInterface ? &context.interface_imports : &context.implementation_imports;
for (const auto& unit : node.units) add_to(*target);
{
symbol::UnitImport import;
import.unit_name = unit.name;
import.location = unit.location;
target->push_back(std::move(import));
}
} }
void Analyzer::VisitIdentifier(ast::Identifier& node) void Analyzer::VisitIdentifier(ast::Identifier& node)
@ -1167,14 +1174,7 @@ namespace lsp::language::semantic
std::optional<symbol::SymbolId> Analyzer::ResolveFromUses(const std::string& name) std::optional<symbol::SymbolId> Analyzer::ResolveFromUses(const std::string& name)
{ {
if (!current_unit_context_) auto resolve_from_imports = [&](const std::vector<symbol::UnitImport>& imports) -> std::optional<symbol::SymbolId> {
{
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) for (auto it = imports.rbegin(); it != imports.rend(); ++it)
{ {
@ -1228,6 +1228,27 @@ namespace lsp::language::semantic
} }
} }
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))
{
return result;
}
}
if (!file_imports_.empty())
{
if (auto result = resolve_from_imports(file_imports_))
{
return result;
}
}
return std::nullopt; return std::nullopt;
} }

View File

@ -681,6 +681,7 @@ export namespace lsp::language::semantic
std::optional<symbol::SymbolId> current_class_id_; std::optional<symbol::SymbolId> current_class_id_;
std::optional<UnitContext> current_unit_context_; std::optional<UnitContext> current_unit_context_;
std::optional<symbol::UnitVisibility> current_unit_section_; std::optional<symbol::UnitVisibility> current_unit_section_;
std::vector<symbol::UnitImport> file_imports_;
ExternalSymbolProvider external_symbol_provider_; ExternalSymbolProvider external_symbol_provider_;
std::unordered_map<std::string, symbol::SymbolId> imported_symbols_; std::unordered_map<std::string, symbol::SymbolId> imported_symbols_;
}; };

View File

@ -424,6 +424,30 @@ namespace lsp::language::symbol
Builder::ScopeGuard Builder::EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range) 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<Symbol*>(table_.definition(symbol_id));
if (owner_symbol)
{
if (auto* fn = owner_symbol->As<Function>())
{
fn->imports.insert(fn->imports.begin(), file_imports_.begin(), file_imports_.end());
}
else if (auto* method = owner_symbol->As<Method>())
{
method->imports.insert(method->imports.begin(), file_imports_.begin(), file_imports_.end());
}
else if (auto* cls = owner_symbol->As<Class>())
{
cls->imports.insert(cls->imports.begin(), file_imports_.begin(), file_imports_.end());
}
else if (auto* unit = owner_symbol->As<Unit>())
{
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); current_scope_id_ = table_.CreateScope(kind, range, current_scope_id_, symbol_id);
return ScopeGuard(*this, current_scope_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_); const auto* scope_info = table_.scopes().scope(current_scope_id_);
if (!scope_info || !scope_info->owner) 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; return;
} }

View File

@ -608,6 +608,9 @@ export namespace lsp::language::symbol
std::optional<SymbolId> current_parent_symbol_id_; std::optional<SymbolId> current_parent_symbol_id_;
std::optional<SymbolId> current_function_id_; std::optional<SymbolId> current_function_id_;
std::vector<UnitImport> file_imports_;
std::unordered_set<SymbolId> file_imports_applied_;
bool in_interface_section_; bool in_interface_section_;
ast::AccessModifier current_access_modifier_ = ast::AccessModifier::kPublic; ast::AccessModifier current_access_modifier_ = ast::AccessModifier::kPublic;
}; };