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}