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::{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                let args = if let Some(generic_arguments) = segment.generic_arguments() {
164                    compile_generic_arguments(ctx, &generic_arguments)
165                } else {
166                    vec![]
167                };
168
169                let ty = ctx.symbol_type(symbol);
170
171                if !args.is_empty() {
172                    if let Symbol::Function(function) = ctx.symbol(symbol).clone() {
173                        if function.vars.len() != args.len() {
174                            ctx.diagnostic(
175                                &name,
176                                DiagnosticKind::ExpectedGenericArguments(
177                                    function.vars.len(),
178                                    args.len(),
179                                ),
180                            );
181                        }
182
183                        let mut map = HashMap::new();
184
185                        for (i, arg) in args.iter().enumerate() {
186                            if i >= function.vars.len() {
187                                break;
188                            }
189
190                            map.insert(function.vars[i], *arg);
191                        }
192
193                        override_type = Some(ctx.alloc_type(Type::Apply(Apply::new(ty, map))));
194                    } else {
195                        ctx.diagnostic(&name, DiagnosticKind::GenericArgumentsOnSymbolReference);
196                        return PathResult::Unresolved;
197                    }
198                }
199
200                value = Some(PathResult::Symbol(symbol, override_type));
201            }
202            PathResult::Type(mut ty) => {
203                let args = if let Some(generic_arguments) = segment.generic_arguments() {
204                    compile_generic_arguments(ctx, &generic_arguments)
205                } else {
206                    vec![]
207                };
208
209                let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, false);
210
211                let params = match ctx.ty(semantic) {
212                    Type::Apply(_) | Type::Ref(_) => unreachable!(),
213                    Type::Unresolved => return PathResult::Unresolved,
214                    Type::Atom(..)
215                    | Type::Pair(..)
216                    | Type::Function(..)
217                    | Type::Generic(_)
218                    | Type::Union(..)
219                    | Type::Never
220                    | Type::Any => {
221                        vec![]
222                    }
223                    Type::Alias(alias) => alias.generics.clone(),
224                    Type::Struct(ty) => ty.generics.clone(),
225                };
226
227                if params.len() != args.len() {
228                    ctx.diagnostic(
229                        &name,
230                        DiagnosticKind::ExpectedGenericArguments(params.len(), args.len()),
231                    );
232                }
233
234                if !params.is_empty() {
235                    let mut map = HashMap::new();
236                    for (i, param) in params.into_iter().enumerate() {
237                        let arg = args.get(i).copied().unwrap_or_else(|| {
238                            debug!("Unresolved type due to missing generic argument");
239                            ctx.builtins().unresolved.ty
240                        });
241                        map.insert(param, arg);
242                    }
243                    ty = ctx.alloc_type(Type::Apply(Apply::new(ty, map)));
244                }
245
246                value = Some(PathResult::Type(ty));
247            }
248        }
249
250        previous_name = Some(name.text().to_string());
251    }
252
253    let value = value.unwrap_or(PathResult::Unresolved);
254
255    match (value, kind) {
256        (PathResult::Symbol(_, _), PathKind::Type) => {
257            ctx.diagnostic(range, DiagnosticKind::ExpectedType(previous_name.unwrap()));
258            PathResult::Unresolved
259        }
260        (PathResult::Type(_), PathKind::Symbol) => {
261            ctx.diagnostic(
262                range,
263                DiagnosticKind::ExpectedSymbol(previous_name.unwrap()),
264            );
265            PathResult::Unresolved
266        }
267        _ => value,
268    }
269}