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}