1use std::collections::HashMap;
2
3use log::debug;
4use rowan::TextRange;
5use rue_ast::{AstGenericArguments, AstNode, AstPathSegment};
6use rue_diagnostic::DiagnosticKind;
7use rue_hir::{Declaration, ImportId, Symbol, SymbolId};
8use rue_parser::SyntaxToken;
9use rue_types::{Apply, Type, TypeId};
10
11use crate::{Compiler, CompletionContext, GetTextRange, SyntaxItemKind, compile_generic_arguments};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum PathKind {
15 Symbol,
16 Type,
17}
18
19#[derive(Debug, Clone, Copy)]
20pub enum PathResult {
21 Unresolved,
22 Symbol(SymbolId, Option<TypeId>, Option<ImportId>),
23 Type(TypeId, Option<ImportId>),
24}
25
26pub trait PathSegment {
27 fn separator(&self) -> Option<TextRange>;
28 fn text_range(&self) -> TextRange;
29 fn name(&self) -> Option<SyntaxToken>;
30 fn generic_arguments(&self) -> Option<AstGenericArguments>;
31}
32
33impl PathSegment for AstPathSegment {
34 fn separator(&self) -> Option<TextRange> {
35 self.separator().map(|separator| separator.text_range())
36 }
37
38 fn text_range(&self) -> TextRange {
39 self.syntax().text_range()
40 }
41
42 fn name(&self) -> Option<SyntaxToken> {
43 self.name()
44 }
45
46 fn generic_arguments(&self) -> Option<AstGenericArguments> {
47 self.generic_arguments()
48 }
49}
50
51impl PathSegment for SyntaxToken {
52 fn separator(&self) -> Option<TextRange> {
53 None
54 }
55
56 fn text_range(&self) -> TextRange {
57 self.text_range()
58 }
59
60 fn name(&self) -> Option<SyntaxToken> {
61 Some(self.clone())
62 }
63
64 fn generic_arguments(&self) -> Option<AstGenericArguments> {
65 None
66 }
67}
68
69pub fn compile_path<S>(
70 ctx: &mut Compiler,
71 range: &impl GetTextRange,
72 segments: impl Iterator<Item = S>,
73 kind: PathKind,
74 mut completions: bool,
75) -> PathResult
76where
77 S: PathSegment,
78{
79 let mut segments = segments.collect::<Vec<_>>();
80 let mut base_scope = ctx.last_scope_id();
81 let mut module_stack = ctx.parent_module_stack().to_vec();
82 let mut path_completion_module = None;
83
84 let length = segments.len();
85
86 if let Some(segment) = segments.first()
87 && let Some(separator) = segment.separator()
88 {
89 ctx.diagnostic(&separator, DiagnosticKind::PathSeparatorInFirstSegment);
90 }
91
92 while let Some(segment) = segments.first()
93 && let Some(name) = segment.name()
94 && name.text() == "super"
95 {
96 if let Some(module) = path_completion_module
97 && completions
98 && let Some(separator) = segment.separator()
99 {
100 ctx.add_syntax(
101 SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports {
102 module,
103 allow_super: true,
104 }),
105 TextRange::new(separator.end(), segment.text_range().end()),
106 );
107 }
108
109 segments.remove(0);
110
111 if let Some(module) = module_stack.pop() {
112 base_scope = ctx.module(module).scope;
113 path_completion_module = Some(module);
114
115 ctx.add_syntax(SyntaxItemKind::SymbolReference(module), name.text_range());
116 } else {
117 ctx.diagnostic(&name, DiagnosticKind::UnresolvedSuper);
118 completions = false;
119 }
120 }
121
122 let mut value = None;
123 let mut previous_name = None;
124
125 for (index, segment) in segments.iter().enumerate() {
126 if let Some(module) = path_completion_module
127 && completions
128 && let Some(separator) = segment.separator()
129 {
130 ctx.add_syntax(
131 SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports {
132 module,
133 allow_super: index == 0,
134 }),
135 TextRange::new(separator.end(), segment.text_range().end()),
136 );
137 }
138
139 if let Some(name) = segment.name()
140 && name.text() == "super"
141 {
142 if index == length - 1 {
143 ctx.diagnostic(&name, DiagnosticKind::SuperAtEnd);
144 } else {
145 ctx.diagnostic(&name, DiagnosticKind::SuperAfterNamedImport);
146 }
147 return PathResult::Unresolved;
148 }
149
150 let Some(name) = segment.name() else {
151 return PathResult::Unresolved;
152 };
153
154 let (symbol, ty) = if let Some(value) = value {
155 if let PathResult::Symbol(symbol, _, _) = value
156 && let Symbol::Module(module) = ctx.symbol(symbol)
157 {
158 let scope = ctx.scope(module.scope);
159 let symbol = scope.symbol(name.text()).map(|symbol| {
160 (
161 symbol,
162 scope.is_symbol_exported(symbol),
163 scope.symbol_import(symbol),
164 )
165 });
166 let ty = scope
167 .ty(name.text())
168 .map(|ty| (ty, scope.is_type_exported(ty), scope.type_import(ty)));
169 (symbol, ty)
170 } else {
171 ctx.diagnostic(
172 &name,
173 DiagnosticKind::SubpathNotSupported(previous_name.unwrap()),
174 );
175 return PathResult::Unresolved;
176 }
177 } else if base_scope == ctx.last_scope_id() {
178 let symbol = ctx
179 .resolve_symbol_in(base_scope, name.text())
180 .map(|(symbol, import)| (symbol, true, import));
181 let ty = ctx
182 .resolve_type_in(base_scope, name.text())
183 .map(|(ty, import)| (ty, true, import));
184 (symbol, ty)
185 } else {
186 let base = ctx.scope(base_scope);
187 let symbol = base
188 .symbol(name.text())
189 .filter(|s| base.is_symbol_exported(*s))
190 .map(|s| (s, true, base.symbol_import(s)));
191 let ty = base
192 .ty(name.text())
193 .filter(|t| base.is_type_exported(*t))
194 .map(|t| (t, true, base.type_import(t)));
195 (symbol, ty)
196 };
197
198 let initial = match (symbol, ty) {
199 (Some((symbol, _, symbol_import)), Some((ty, _, type_import))) => match kind {
200 PathKind::Symbol => PathResult::Symbol(symbol, None, symbol_import),
201 PathKind::Type => PathResult::Type(ty, type_import),
202 },
203 (Some((symbol, _, symbol_import)), None) => {
204 PathResult::Symbol(symbol, None, symbol_import)
205 }
206 (None, Some((ty, _, type_import))) => PathResult::Type(ty, type_import),
207 (None, None) => {
208 match kind {
209 PathKind::Symbol => ctx.diagnostic(
210 &name,
211 DiagnosticKind::UndeclaredSymbol(name.text().to_string()),
212 ),
213 PathKind::Type => ctx.diagnostic(
214 &name,
215 DiagnosticKind::UndeclaredType(name.text().to_string()),
216 ),
217 }
218 PathResult::Unresolved
219 }
220 };
221
222 if matches!(initial, PathResult::Symbol(_, _, _))
223 && let Some((_, is_exported, _)) = symbol
224 && !is_exported
225 {
226 ctx.diagnostic(
227 &name,
228 DiagnosticKind::PrivateSymbol(
229 name.text().to_string(),
230 previous_name.clone().unwrap(),
231 ),
232 );
233 }
234
235 if matches!(initial, PathResult::Type(_, _))
236 && let Some((_, is_exported, _)) = ty
237 && !is_exported
238 {
239 ctx.diagnostic(
240 &name,
241 DiagnosticKind::PrivateType(
242 name.text().to_string(),
243 previous_name.clone().unwrap(),
244 ),
245 );
246 }
247
248 match initial {
249 PathResult::Unresolved => return PathResult::Unresolved,
250 PathResult::Symbol(symbol, mut override_type, import) => {
251 ctx.reference(Declaration::Symbol(symbol), import);
252 ctx.reference_span(Declaration::Symbol(symbol), name.text_range());
253
254 let args = if let Some(generic_arguments) = segment.generic_arguments() {
255 compile_generic_arguments(ctx, &generic_arguments)
256 } else {
257 vec![]
258 };
259
260 let ty = ctx.symbol_type(symbol);
261
262 if !args.is_empty() {
263 if let Symbol::Function(function) = ctx.symbol(symbol).clone() {
264 if function.vars.len() != args.len() {
265 ctx.diagnostic(
266 &name,
267 DiagnosticKind::ExpectedGenericArguments(
268 function.vars.len(),
269 args.len(),
270 ),
271 );
272 }
273
274 let mut map = HashMap::new();
275
276 for (i, arg) in args.iter().enumerate() {
277 if i >= function.vars.len() {
278 break;
279 }
280
281 map.insert(function.vars[i], *arg);
282 }
283
284 override_type = Some(ctx.alloc_type(Type::Apply(Apply::new(ty, map))));
285 } else {
286 ctx.diagnostic(&name, DiagnosticKind::GenericArgumentsOnSymbolReference);
287 return PathResult::Unresolved;
288 }
289 }
290
291 if matches!(ctx.symbol(symbol), Symbol::Module(_)) {
292 path_completion_module = Some(symbol);
293 }
294
295 value = Some(PathResult::Symbol(symbol, override_type, import));
296 }
297 PathResult::Type(mut ty, import) => {
298 ctx.reference(Declaration::Type(ty), import);
299 ctx.reference_span(Declaration::Type(ty), name.text_range());
300
301 let args = if let Some(generic_arguments) = segment.generic_arguments() {
302 compile_generic_arguments(ctx, &generic_arguments)
303 } else {
304 vec![]
305 };
306
307 let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, false);
308
309 let params = match ctx.ty(semantic) {
310 Type::Apply(_) | Type::Ref(_) => unreachable!(),
311 Type::Unresolved => return PathResult::Unresolved,
312 Type::Atom(..)
313 | Type::Pair(..)
314 | Type::Function(..)
315 | Type::Generic(_)
316 | Type::Union(..)
317 | Type::Never
318 | Type::Any => {
319 vec![]
320 }
321 Type::Alias(alias) => alias.generics.clone(),
322 Type::Struct(ty) => ty.generics.clone(),
323 };
324
325 if params.len() != args.len() {
326 ctx.diagnostic(
327 &name,
328 DiagnosticKind::ExpectedGenericArguments(params.len(), args.len()),
329 );
330 }
331
332 if !params.is_empty() {
333 let mut map = HashMap::new();
334 for (i, param) in params.into_iter().enumerate() {
335 let arg = args.get(i).copied().unwrap_or_else(|| {
336 debug!("Unresolved type due to missing generic argument");
337 ctx.builtins().unresolved.ty
338 });
339 map.insert(param, arg);
340 }
341 ty = ctx.alloc_type(Type::Apply(Apply::new(ty, map)));
342 }
343
344 value = Some(PathResult::Type(ty, import));
345 }
346 }
347
348 previous_name = Some(name.text().to_string());
349 }
350
351 let value = value.unwrap_or(PathResult::Unresolved);
352
353 match (value, kind) {
354 (PathResult::Symbol(_, _, _), PathKind::Type) => {
355 ctx.diagnostic(range, DiagnosticKind::ExpectedType(previous_name.unwrap()));
356 PathResult::Unresolved
357 }
358 (PathResult::Type(_, _), PathKind::Symbol) => {
359 ctx.diagnostic(
360 range,
361 DiagnosticKind::ExpectedSymbol(previous_name.unwrap()),
362 );
363 PathResult::Unresolved
364 }
365 _ => value,
366 }
367}