1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use crate::semantic::resolver::Resolver;
use crate::semantic::symbol_table::Symbol;
impl<'a> Resolver<'a> {
// ============================================================
// Import Path Utilities
// ============================================================
/// Parse an import path into components (split by ::)
pub fn parse_import_path(path: &str) -> Vec<String> {
path.split("::").map(|s| s.to_string()).collect()
}
/// Check if an import is a wildcard import (ends with *)
pub fn is_wildcard_import(path: &str) -> bool {
path.ends_with("::*") || path == "*"
}
// ============================================================
// Import Resolution (for expanding import statements)
// ============================================================
/// Resolve an import path to the symbols it brings into scope
pub fn resolve_import(&self, import_path: &str) -> Vec<String> {
if Self::is_wildcard_import(import_path) {
self.resolve_wildcard_import(import_path)
} else if self.resolve_qualified(import_path).is_some() {
vec![import_path.to_string()]
} else {
vec![]
}
}
fn resolve_wildcard_import(&self, import_path: &str) -> Vec<String> {
if import_path == "*" {
return self
.symbol_table()
.iter_symbols()
.filter_map(|symbol| {
let qname = symbol.qualified_name();
if !qname.contains("::") {
Some(qname.to_string())
} else {
None
}
})
.collect();
}
let prefix = import_path.strip_suffix("::*").unwrap_or(import_path);
self.symbol_table()
.iter_symbols()
.filter_map(|symbol| {
let qname = symbol.qualified_name();
if let Some(remainder) = qname.strip_prefix(prefix)
&& let Some(remainder) = remainder.strip_prefix("::")
&& !remainder.contains("::")
{
return Some(qname.to_string());
}
None
})
.collect()
}
// ============================================================
// Import-based Name Resolution
// ============================================================
/// Resolve a simple name via imports registered in a scope.
/// Checks each import to see if it brings `name` into scope.
pub(super) fn resolve_via_imports(&self, name: &str, scope_id: usize) -> Option<&Symbol> {
self.resolve_via_imports_filtered(name, scope_id, false)
}
/// Resolve a simple name via public imports only (for re-export resolution).
/// Uses direct symbol table lookup to avoid recursion.
pub(super) fn resolve_via_public_imports(
&self,
name: &str,
scope_id: usize,
) -> Option<&Symbol> {
self.symbol_table()
.get_scope_imports(scope_id)
.iter()
.filter(|import| import.is_public)
.find_map(|import| {
if import.is_namespace {
// For wildcard imports, try direct lookup only (no recursion)
let namespace = import.path.trim_end_matches("::*").trim_end_matches("::**");
let qualified = format!("{namespace}::{name}");
self.symbol_table().find_by_qualified_name(&qualified)
} else {
// For direct imports, check if it imports this name
let imports_this_name =
import.path.ends_with(&format!("::{name}")) || import.path == name;
imports_this_name
.then(|| self.symbol_table().find_by_qualified_name(&import.path))?
}
})
}
/// Resolve a simple name via imports, optionally filtering to public imports only.
fn resolve_via_imports_filtered(
&self,
name: &str,
scope_id: usize,
public_only: bool,
) -> Option<&Symbol> {
self.symbol_table()
.get_scope_imports(scope_id)
.iter()
.filter(|import| !public_only || import.is_public)
.find_map(|import| {
if import.is_namespace {
self.try_wildcard_import(name, &import.path, import.is_recursive)
} else {
self.try_direct_import(name, &import.path)
}
})
}
/// Check if a wildcard import (`Pkg::*` or `Pkg::**`) provides `name`.
fn try_wildcard_import(
&self,
name: &str,
import_path: &str,
is_recursive: bool,
) -> Option<&Symbol> {
let namespace = import_path.trim_end_matches("::*").trim_end_matches("::**");
let qualified = format!("{namespace}::{name}");
self.resolve_qualified(&qualified)
.or_else(|| is_recursive.then(|| self.search_nested_namespaces(name, namespace))?)
}
/// Check if a direct import (`Pkg::Member`) provides `name`.
fn try_direct_import(&self, name: &str, import_path: &str) -> Option<&Symbol> {
// Direct import "Pkg::Foo" makes "Foo" available
let imports_this_name = import_path.ends_with(&format!("::{name}")) || import_path == name;
imports_this_name.then(|| self.resolve_qualified(import_path))?
}
/// Search all nested namespaces under `namespace` for a symbol named `name`.
/// Used for recursive imports (`Pkg::**`).
fn search_nested_namespaces(&self, name: &str, namespace: &str) -> Option<&Symbol> {
let prefix = format!("{namespace}::");
let suffix = format!("::{name}");
// Search through all symbols in the arena
self.symbol_table().iter_symbols().find(|symbol| {
let qname = symbol.qualified_name();
qname.starts_with(&prefix) && qname.ends_with(&suffix)
})
}
}