rue_compiler/compile/
path.rs

1use std::collections::HashMap;
2
3use log::debug;
4use rue_ast::{AstNode, AstPathSegment};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{Symbol, SymbolId};
7use rue_types::{Apply, Type, TypeId};
8
9use crate::{Compiler, GetTextRange, compile_generic_arguments};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum PathKind {
13    Symbol,
14    Type,
15}
16
17#[derive(Debug, Clone, Copy)]
18pub enum PathResult {
19    Unresolved,
20    Symbol(SymbolId),
21    Type(TypeId),
22}
23
24pub fn compile_path(
25    ctx: &mut Compiler,
26    range: &impl GetTextRange,
27    segments: impl Iterator<Item = AstPathSegment>,
28    kind: PathKind,
29) -> PathResult {
30    let segments = &segments.collect::<Vec<_>>();
31
32    let mut value = None;
33    let mut previous_name = None;
34
35    for (index, segment) in segments.iter().enumerate() {
36        if index == 0
37            && let Some(separator) = segment.initial_separator()
38        {
39            ctx.diagnostic(
40                separator.syntax(),
41                DiagnosticKind::PathSeparatorInFirstSegment,
42            );
43            return PathResult::Unresolved;
44        }
45
46        let Some(name) = segment.name() else {
47            return PathResult::Unresolved;
48        };
49
50        let (symbol, ty) = if let Some(value) = value {
51            if let PathResult::Symbol(symbol) = value
52                && let Symbol::Module(module) = ctx.symbol(symbol)
53            {
54                let scope = ctx.scope(module.scope);
55                let symbol = scope
56                    .symbol(name.text())
57                    .map(|symbol| (symbol, scope.is_symbol_exported(symbol)));
58                let ty = scope
59                    .ty(name.text())
60                    .map(|ty| (ty, scope.is_type_exported(ty)));
61                (symbol, ty)
62            } else {
63                ctx.diagnostic(
64                    &name,
65                    DiagnosticKind::SubpathNotSupported(previous_name.unwrap()),
66                );
67                return PathResult::Unresolved;
68            }
69        } else {
70            let symbol = ctx.resolve_symbol(name.text()).map(|symbol| (symbol, true));
71            let ty = ctx.resolve_type(name.text()).map(|ty| (ty, true));
72            (symbol, ty)
73        };
74
75        let initial = match (symbol, ty) {
76            (Some((symbol, _)), Some((ty, _))) => match kind {
77                PathKind::Symbol => PathResult::Symbol(symbol),
78                PathKind::Type => PathResult::Type(ty),
79            },
80            (Some((symbol, _)), None) => PathResult::Symbol(symbol),
81            (None, Some((ty, _))) => PathResult::Type(ty),
82            (None, None) => {
83                match kind {
84                    PathKind::Symbol => ctx.diagnostic(
85                        &name,
86                        DiagnosticKind::UndeclaredSymbol(name.text().to_string()),
87                    ),
88                    PathKind::Type => ctx.diagnostic(
89                        &name,
90                        DiagnosticKind::UndeclaredType(name.text().to_string()),
91                    ),
92                }
93                PathResult::Unresolved
94            }
95        };
96
97        if matches!(initial, PathResult::Symbol(_))
98            && let Some((_, is_exported)) = symbol
99            && !is_exported
100        {
101            ctx.diagnostic(
102                &name,
103                DiagnosticKind::PrivateSymbol(
104                    name.text().to_string(),
105                    previous_name.clone().unwrap(),
106                ),
107            );
108        }
109
110        if matches!(initial, PathResult::Type(_))
111            && let Some((_, is_exported)) = ty
112            && !is_exported
113        {
114            ctx.diagnostic(
115                &name,
116                DiagnosticKind::PrivateType(
117                    name.text().to_string(),
118                    previous_name.clone().unwrap(),
119                ),
120            );
121        }
122
123        match initial {
124            PathResult::Unresolved => return PathResult::Unresolved,
125            PathResult::Symbol(symbol) => {
126                let args = if let Some(generic_arguments) = segment.generic_arguments() {
127                    compile_generic_arguments(ctx, &generic_arguments)
128                } else {
129                    vec![]
130                };
131
132                if !args.is_empty() {
133                    ctx.diagnostic(&name, DiagnosticKind::GenericArgumentsOnSymbolReference);
134                    return PathResult::Unresolved;
135                }
136
137                value = Some(PathResult::Symbol(symbol));
138            }
139            PathResult::Type(mut ty) => {
140                let args = if let Some(generic_arguments) = segment.generic_arguments() {
141                    compile_generic_arguments(ctx, &generic_arguments)
142                } else {
143                    vec![]
144                };
145
146                let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, false);
147
148                let params = match ctx.ty(semantic) {
149                    Type::Apply(_) | Type::Ref(_) => unreachable!(),
150                    Type::Unresolved => return PathResult::Unresolved,
151                    Type::Atom(..)
152                    | Type::Pair(..)
153                    | Type::Function(..)
154                    | Type::Generic
155                    | Type::Union(..)
156                    | Type::Never => {
157                        vec![]
158                    }
159                    Type::Alias(alias) => alias.generics.clone(),
160                    Type::Struct(ty) => ty.generics.clone(),
161                };
162
163                if params.len() != args.len() {
164                    ctx.diagnostic(
165                        &name,
166                        DiagnosticKind::ExpectedGenericArguments(params.len(), args.len()),
167                    );
168                }
169
170                if !params.is_empty() {
171                    let mut map = HashMap::new();
172                    for (i, param) in params.into_iter().enumerate() {
173                        let arg = args.get(i).copied().unwrap_or_else(|| {
174                            debug!("Unresolved type due to missing generic argument");
175                            ctx.builtins().unresolved.ty
176                        });
177                        map.insert(param, arg);
178                    }
179                    ty = ctx.alloc_type(Type::Apply(Apply::new(ty, map)));
180                }
181
182                value = Some(PathResult::Type(ty));
183            }
184        }
185
186        previous_name = Some(name.text().to_string());
187    }
188
189    let value = value.unwrap_or(PathResult::Unresolved);
190
191    match (value, kind) {
192        (PathResult::Symbol(_), PathKind::Type) => {
193            ctx.diagnostic(range, DiagnosticKind::ExpectedType(previous_name.unwrap()));
194            PathResult::Unresolved
195        }
196        (PathResult::Type(_), PathKind::Symbol) => {
197            ctx.diagnostic(
198                range,
199                DiagnosticKind::ExpectedSymbol(previous_name.unwrap()),
200            );
201            PathResult::Unresolved
202        }
203        _ => value,
204    }
205}