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, Symbol, SymbolId};
8use rue_parser::SyntaxToken;
9use rue_types::{Apply, Type, TypeId};
10
11use crate::{Compiler, GetTextRange, 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>),
23    Type(TypeId),
24}
25
26pub trait PathSegment {
27    fn initial_separator(&self) -> Option<TextRange>;
28    fn name(&self) -> Option<SyntaxToken>;
29    fn generic_arguments(&self) -> Option<AstGenericArguments>;
30}
31
32impl PathSegment for AstPathSegment {
33    fn initial_separator(&self) -> Option<TextRange> {
34        self.initial_separator()
35            .map(|separator| separator.syntax().text_range())
36    }
37
38    fn name(&self) -> Option<SyntaxToken> {
39        self.name()
40    }
41
42    fn generic_arguments(&self) -> Option<AstGenericArguments> {
43        self.generic_arguments()
44    }
45}
46
47impl PathSegment for SyntaxToken {
48    fn initial_separator(&self) -> Option<TextRange> {
49        None
50    }
51
52    fn name(&self) -> Option<SyntaxToken> {
53        Some(self.clone())
54    }
55
56    fn generic_arguments(&self) -> Option<AstGenericArguments> {
57        None
58    }
59}
60
61pub fn compile_path<S>(
62    ctx: &mut Compiler,
63    range: &impl GetTextRange,
64    segments: impl Iterator<Item = S>,
65    kind: PathKind,
66) -> PathResult
67where
68    S: PathSegment,
69{
70    let segments = &segments.collect::<Vec<_>>();
71
72    let mut value = None;
73    let mut previous_name = None;
74
75    for (index, segment) in segments.iter().enumerate() {
76        if index == 0
77            && let Some(separator) = segment.initial_separator()
78        {
79            ctx.diagnostic(&separator, DiagnosticKind::PathSeparatorInFirstSegment);
80            return PathResult::Unresolved;
81        }
82
83        let Some(name) = segment.name() else {
84            return PathResult::Unresolved;
85        };
86
87        let (symbol, ty) = if let Some(value) = value {
88            if let PathResult::Symbol(symbol, _) = value
89                && let Symbol::Module(module) = ctx.symbol(symbol)
90            {
91                let scope = ctx.scope(module.scope);
92                let symbol = scope
93                    .symbol(name.text())
94                    .map(|symbol| (symbol, scope.is_symbol_exported(symbol)));
95                let ty = scope
96                    .ty(name.text())
97                    .map(|ty| (ty, scope.is_type_exported(ty)));
98                (symbol, ty)
99            } else {
100                ctx.diagnostic(
101                    &name,
102                    DiagnosticKind::SubpathNotSupported(previous_name.unwrap()),
103                );
104                return PathResult::Unresolved;
105            }
106        } else {
107            let symbol = ctx.resolve_symbol(name.text()).map(|symbol| (symbol, true));
108            let ty = ctx.resolve_type(name.text()).map(|ty| (ty, true));
109            (symbol, ty)
110        };
111
112        let initial = match (symbol, ty) {
113            (Some((symbol, _)), Some((ty, _))) => match kind {
114                PathKind::Symbol => PathResult::Symbol(symbol, None),
115                PathKind::Type => PathResult::Type(ty),
116            },
117            (Some((symbol, _)), None) => PathResult::Symbol(symbol, None),
118            (None, Some((ty, _))) => PathResult::Type(ty),
119            (None, None) => {
120                match kind {
121                    PathKind::Symbol => ctx.diagnostic(
122                        &name,
123                        DiagnosticKind::UndeclaredSymbol(name.text().to_string()),
124                    ),
125                    PathKind::Type => ctx.diagnostic(
126                        &name,
127                        DiagnosticKind::UndeclaredType(name.text().to_string()),
128                    ),
129                }
130                PathResult::Unresolved
131            }
132        };
133
134        if matches!(initial, PathResult::Symbol(_, _))
135            && let Some((_, is_exported)) = symbol
136            && !is_exported
137        {
138            ctx.diagnostic(
139                &name,
140                DiagnosticKind::PrivateSymbol(
141                    name.text().to_string(),
142                    previous_name.clone().unwrap(),
143                ),
144            );
145        }
146
147        if matches!(initial, PathResult::Type(_))
148            && let Some((_, is_exported)) = ty
149            && !is_exported
150        {
151            ctx.diagnostic(
152                &name,
153                DiagnosticKind::PrivateType(
154                    name.text().to_string(),
155                    previous_name.clone().unwrap(),
156                ),
157            );
158        }
159
160        match initial {
161            PathResult::Unresolved => return PathResult::Unresolved,
162            PathResult::Symbol(symbol, mut override_type) => {
163                ctx.reference(Declaration::Symbol(symbol));
164                ctx.reference_span(Declaration::Symbol(symbol), name.text_range());
165
166                let args = if let Some(generic_arguments) = segment.generic_arguments() {
167                    compile_generic_arguments(ctx, &generic_arguments)
168                } else {
169                    vec![]
170                };
171
172                let ty = ctx.symbol_type(symbol);
173
174                if !args.is_empty() {
175                    if let Symbol::Function(function) = ctx.symbol(symbol).clone() {
176                        if function.vars.len() != args.len() {
177                            ctx.diagnostic(
178                                &name,
179                                DiagnosticKind::ExpectedGenericArguments(
180                                    function.vars.len(),
181                                    args.len(),
182                                ),
183                            );
184                        }
185
186                        let mut map = HashMap::new();
187
188                        for (i, arg) in args.iter().enumerate() {
189                            if i >= function.vars.len() {
190                                break;
191                            }
192
193                            map.insert(function.vars[i], *arg);
194                        }
195
196                        override_type = Some(ctx.alloc_type(Type::Apply(Apply::new(ty, map))));
197                    } else {
198                        ctx.diagnostic(&name, DiagnosticKind::GenericArgumentsOnSymbolReference);
199                        return PathResult::Unresolved;
200                    }
201                }
202
203                value = Some(PathResult::Symbol(symbol, override_type));
204            }
205            PathResult::Type(mut ty) => {
206                ctx.reference(Declaration::Type(ty));
207                ctx.reference_span(Declaration::Type(ty), name.text_range());
208
209                let args = if let Some(generic_arguments) = segment.generic_arguments() {
210                    compile_generic_arguments(ctx, &generic_arguments)
211                } else {
212                    vec![]
213                };
214
215                let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, false);
216
217                let params = match ctx.ty(semantic) {
218                    Type::Apply(_) | Type::Ref(_) => unreachable!(),
219                    Type::Unresolved => return PathResult::Unresolved,
220                    Type::Atom(..)
221                    | Type::Pair(..)
222                    | Type::Function(..)
223                    | Type::Generic(_)
224                    | Type::Union(..)
225                    | Type::Never
226                    | Type::Any => {
227                        vec![]
228                    }
229                    Type::Alias(alias) => alias.generics.clone(),
230                    Type::Struct(ty) => ty.generics.clone(),
231                };
232
233                if params.len() != args.len() {
234                    ctx.diagnostic(
235                        &name,
236                        DiagnosticKind::ExpectedGenericArguments(params.len(), args.len()),
237                    );
238                }
239
240                if !params.is_empty() {
241                    let mut map = HashMap::new();
242                    for (i, param) in params.into_iter().enumerate() {
243                        let arg = args.get(i).copied().unwrap_or_else(|| {
244                            debug!("Unresolved type due to missing generic argument");
245                            ctx.builtins().unresolved.ty
246                        });
247                        map.insert(param, arg);
248                    }
249                    ty = ctx.alloc_type(Type::Apply(Apply::new(ty, map)));
250                }
251
252                value = Some(PathResult::Type(ty));
253            }
254        }
255
256        previous_name = Some(name.text().to_string());
257    }
258
259    let value = value.unwrap_or(PathResult::Unresolved);
260
261    match (value, kind) {
262        (PathResult::Symbol(_, _), PathKind::Type) => {
263            ctx.diagnostic(range, DiagnosticKind::ExpectedType(previous_name.unwrap()));
264            PathResult::Unresolved
265        }
266        (PathResult::Type(_), PathKind::Symbol) => {
267            ctx.diagnostic(
268                range,
269                DiagnosticKind::ExpectedSymbol(previous_name.unwrap()),
270            );
271            PathResult::Unresolved
272        }
273        _ => value,
274    }
275}