rue_compiler/compile/
path.rs

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}