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}