🐛 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)
{
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_)
{
// 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<symbol::SymbolId> 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<symbol::UnitImport>& imports) -> std::optional<symbol::SymbolId> {
// 从后往前查找(最后导入的优先级最高)
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<symbol::Unit>())
{
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<symbol::Unit>())
{
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;
}
}

View File

@ -681,6 +681,7 @@ export namespace lsp::language::semantic
std::optional<symbol::SymbolId> current_class_id_;
std::optional<UnitContext> current_unit_context_;
std::optional<symbol::UnitVisibility> current_unit_section_;
std::vector<symbol::UnitImport> file_imports_;
ExternalSymbolProvider external_symbol_provider_;
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)
{
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);
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;
}

View File

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