1use std::collections::HashMap;
2
3use ecow::EcoString;
4use lsp_types::Location;
5
6use crate::{
7 analyse,
8 ast::{
9 self, ArgNames, CustomType, Definition, Function, ModuleConstant, Pattern,
10 RecordConstructor, SrcSpan, TypedExpr, TypedModule, visit::Visit,
11 },
12 build::Located,
13 type_::{
14 ModuleInterface, ModuleValueConstructor, Type, ValueConstructor, ValueConstructorVariant,
15 error::{Named, VariableOrigin},
16 },
17};
18
19use super::{
20 compiler::ModuleSourceInformation, rename::RenameTarget, src_span_to_lsp_range, url_from_path,
21};
22
23pub enum Referenced {
24 LocalVariable {
25 definition_location: SrcSpan,
26 location: SrcSpan,
27 origin: Option<VariableOrigin>,
28 },
29 ModuleValue {
30 module: EcoString,
31 name: EcoString,
32 location: SrcSpan,
33 name_kind: Named,
34 target_kind: RenameTarget,
35 },
36 ModuleType {
37 module: EcoString,
38 name: EcoString,
39 location: SrcSpan,
40 target_kind: RenameTarget,
41 },
42}
43
44pub fn reference_for_ast_node(
45 found: Located<'_>,
46 current_module: &EcoString,
47) -> Option<Referenced> {
48 match found {
49 Located::Expression {
50 expression:
51 TypedExpr::Var {
52 constructor:
53 ValueConstructor {
54 variant:
55 ValueConstructorVariant::LocalVariable {
56 location: definition_location,
57 origin,
58 },
59 ..
60 },
61 location,
62 ..
63 },
64 ..
65 } => Some(Referenced::LocalVariable {
66 definition_location: *definition_location,
67 location: *location,
68 origin: Some(origin.clone()),
69 }),
70 Located::Pattern(Pattern::Variable {
71 location, origin, ..
72 }) => Some(Referenced::LocalVariable {
73 definition_location: *location,
74 location: *location,
75 origin: Some(origin.clone()),
76 }),
77 Located::Pattern(Pattern::VarUsage {
78 constructor,
79 location,
80 ..
81 }) => constructor
82 .as_ref()
83 .and_then(|constructor| match &constructor.variant {
84 ValueConstructorVariant::LocalVariable {
85 location: definition_location,
86 origin,
87 } => Some(Referenced::LocalVariable {
88 definition_location: *definition_location,
89 location: *location,
90 origin: Some(origin.clone()),
91 }),
92 _ => None,
93 }),
94 Located::Pattern(Pattern::Assign { location, .. }) => Some(Referenced::LocalVariable {
95 definition_location: *location,
96 location: *location,
97 origin: None,
98 }),
99 Located::Arg(arg) => match &arg.names {
100 ArgNames::Named { location, .. }
101 | ArgNames::NamedLabelled {
102 name_location: location,
103 ..
104 } => Some(Referenced::LocalVariable {
105 definition_location: *location,
106 location: *location,
107 origin: None,
108 }),
109 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None,
110 },
111 Located::Expression {
112 expression:
113 TypedExpr::Var {
114 constructor:
115 ValueConstructor {
116 variant:
117 ValueConstructorVariant::ModuleConstant { module, .. }
118 | ValueConstructorVariant::ModuleFn { module, .. },
119 ..
120 },
121 name,
122 location,
123 ..
124 },
125 ..
126 } => Some(Referenced::ModuleValue {
127 module: module.clone(),
128 name: name.clone(),
129 location: *location,
130 name_kind: Named::Function,
131 target_kind: RenameTarget::Unqualified,
132 }),
133
134 Located::Expression {
135 expression:
136 TypedExpr::ModuleSelect {
137 module_name,
138 label,
139 constructor:
140 ModuleValueConstructor::Fn { .. } | ModuleValueConstructor::Constant { .. },
141 location,
142 field_start,
143 ..
144 },
145 ..
146 } => Some(Referenced::ModuleValue {
147 module: module_name.clone(),
148 name: label.clone(),
149
150 location: SrcSpan::new(*field_start, location.end),
151 name_kind: Named::Function,
152 target_kind: RenameTarget::Qualified,
153 }),
154 Located::ModuleStatement(
155 Definition::Function(Function {
156 name: Some((location, name)),
157 ..
158 })
159 | Definition::ModuleConstant(ModuleConstant {
160 name,
161 name_location: location,
162 ..
163 }),
164 ) => Some(Referenced::ModuleValue {
165 module: current_module.clone(),
166 name: name.clone(),
167 location: *location,
168 name_kind: Named::Function,
169 target_kind: RenameTarget::Definition,
170 }),
171 Located::Expression {
172 expression:
173 TypedExpr::Var {
174 constructor:
175 ValueConstructor {
176 variant: ValueConstructorVariant::Record { module, name, .. },
177 ..
178 },
179 location,
180 ..
181 },
182 ..
183 } => Some(Referenced::ModuleValue {
184 module: module.clone(),
185 name: name.clone(),
186 location: *location,
187 name_kind: Named::CustomTypeVariant,
188 target_kind: RenameTarget::Unqualified,
189 }),
190 Located::Expression {
191 expression:
192 TypedExpr::ModuleSelect {
193 module_name,
194 label,
195 constructor: ModuleValueConstructor::Record { .. },
196 location,
197 field_start,
198 ..
199 },
200 ..
201 } => Some(Referenced::ModuleValue {
202 module: module_name.clone(),
203 name: label.clone(),
204 location: SrcSpan::new(*field_start, location.end),
205 name_kind: Named::CustomTypeVariant,
206 target_kind: RenameTarget::Qualified,
207 }),
208 Located::VariantConstructorDefinition(RecordConstructor {
209 name,
210 name_location,
211 ..
212 }) => Some(Referenced::ModuleValue {
213 module: current_module.clone(),
214 name: name.clone(),
215 location: *name_location,
216 name_kind: Named::CustomTypeVariant,
217 target_kind: RenameTarget::Definition,
218 }),
219 Located::Pattern(Pattern::Constructor {
220 constructor: analyse::Inferred::Known(constructor),
221 module: module_select,
222 name_location: location,
223 ..
224 }) => Some(Referenced::ModuleValue {
225 module: constructor.module.clone(),
226 name: constructor.name.clone(),
227 location: *location,
228 name_kind: Named::CustomTypeVariant,
229 target_kind: if module_select.is_some() {
230 RenameTarget::Qualified
231 } else {
232 RenameTarget::Unqualified
233 },
234 }),
235 Located::Annotation { ast, type_ } => match type_.named_type_name() {
236 Some((module, name)) => {
237 let (target_kind, location) = match ast {
238 ast::TypeAst::Constructor(constructor) => {
239 let kind = if constructor.module.is_some() {
240 RenameTarget::Qualified
241 } else {
242 RenameTarget::Unqualified
243 };
244 (kind, constructor.name_location)
245 }
246 ast::TypeAst::Fn(_)
247 | ast::TypeAst::Var(_)
248 | ast::TypeAst::Tuple(_)
249 | ast::TypeAst::Hole(_) => (RenameTarget::Unqualified, ast.location()),
250 };
251 Some(Referenced::ModuleType {
252 module,
253 name,
254 location,
255 target_kind,
256 })
257 }
258 None => None,
259 },
260 Located::ModuleStatement(Definition::CustomType(CustomType {
261 name,
262 name_location,
263 ..
264 })) => Some(Referenced::ModuleType {
265 module: current_module.clone(),
266 name: name.clone(),
267 location: *name_location,
268 target_kind: RenameTarget::Definition,
269 }),
270 _ => None,
271 }
272}
273
274pub fn find_module_references(
275 module_name: EcoString,
276 name: EcoString,
277 modules: &im::HashMap<EcoString, ModuleInterface>,
278 sources: &HashMap<EcoString, ModuleSourceInformation>,
279 layer: ast::Layer,
280) -> Vec<Location> {
281 let mut reference_locations = Vec::new();
282
283 for module in modules.values() {
284 if module.name == module_name || module.references.imported_modules.contains(&module_name) {
285 let Some(source_information) = sources.get(&module.name) else {
286 continue;
287 };
288
289 find_references_in_module(
290 &module_name,
291 &name,
292 module,
293 source_information,
294 &mut reference_locations,
295 layer,
296 );
297 }
298 }
299
300 reference_locations
301}
302
303fn find_references_in_module(
304 module_name: &EcoString,
305 name: &EcoString,
306 module: &ModuleInterface,
307 source_information: &ModuleSourceInformation,
308 reference_locations: &mut Vec<Location>,
309 layer: ast::Layer,
310) {
311 let reference_map = match layer {
312 ast::Layer::Value => &module.references.value_references,
313 ast::Layer::Type => &module.references.type_references,
314 };
315
316 let Some(references) = reference_map.get(&(module_name.clone(), name.clone())) else {
317 return;
318 };
319
320 let Some(uri) = url_from_path(source_information.path.as_str()) else {
321 return;
322 };
323
324 for reference in references {
325 reference_locations.push(Location {
326 uri: uri.clone(),
327 range: src_span_to_lsp_range(reference.location, &source_information.line_numbers),
328 });
329 }
330}
331
332#[derive(Debug, Clone, Copy)]
333pub struct VariableReference {
334 pub location: SrcSpan,
335 pub kind: VariableReferenceKind,
336}
337
338#[derive(Debug, Clone, Copy)]
339pub enum VariableReferenceKind {
340 Variable,
341 LabelShorthand,
342}
343
344pub fn find_variable_references(
345 module: &TypedModule,
346 definition_location: SrcSpan,
347) -> Vec<VariableReference> {
348 let mut finder = FindVariableReferences {
349 references: Vec::new(),
350 definition_location,
351 };
352 finder.visit_typed_module(module);
353 finder.references
354}
355
356struct FindVariableReferences {
357 references: Vec<VariableReference>,
358 definition_location: SrcSpan,
359}
360
361impl<'ast> Visit<'ast> for FindVariableReferences {
362 fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) {
363 if fun.full_location().contains(self.definition_location.start) {
364 ast::visit::visit_typed_function(self, fun);
365 }
366 }
367
368 fn visit_typed_expr_var(
369 &mut self,
370 location: &'ast SrcSpan,
371 constructor: &'ast ValueConstructor,
372 _name: &'ast EcoString,
373 ) {
374 match constructor.variant {
375 ValueConstructorVariant::LocalVariable {
376 location: definition_location,
377 ..
378 } if definition_location == self.definition_location => {
379 self.references.push(VariableReference {
380 location: *location,
381 kind: VariableReferenceKind::Variable,
382 })
383 }
384 _ => {}
385 }
386 }
387
388 fn visit_typed_clause_guard_var(
389 &mut self,
390 location: &'ast SrcSpan,
391 _name: &'ast EcoString,
392 _type_: &'ast std::sync::Arc<Type>,
393 definition_location: &'ast SrcSpan,
394 ) {
395 if *definition_location == self.definition_location {
396 self.references.push(VariableReference {
397 location: *location,
398 kind: VariableReferenceKind::Variable,
399 })
400 }
401 }
402
403 fn visit_typed_pattern_var_usage(
404 &mut self,
405 location: &'ast SrcSpan,
406 _name: &'ast EcoString,
407 constructor: &'ast Option<ValueConstructor>,
408 _type_: &'ast std::sync::Arc<Type>,
409 ) {
410 let variant = match constructor {
411 Some(constructor) => &constructor.variant,
412 None => return,
413 };
414 match variant {
415 ValueConstructorVariant::LocalVariable {
416 location: definition_location,
417 ..
418 } if *definition_location == self.definition_location => {
419 self.references.push(VariableReference {
420 location: *location,
421 kind: VariableReferenceKind::Variable,
422 })
423 }
424 _ => {}
425 }
426 }
427
428 fn visit_typed_call_arg(&mut self, arg: &'ast crate::type_::TypedCallArg) {
429 if let TypedExpr::Var {
430 location,
431 constructor,
432 ..
433 } = &arg.value
434 {
435 match &constructor.variant {
436 ValueConstructorVariant::LocalVariable {
437 location: definition_location,
438 ..
439 } if arg.uses_label_shorthand()
440 && *definition_location == self.definition_location =>
441 {
442 self.references.push(VariableReference {
443 location: *location,
444 kind: VariableReferenceKind::LabelShorthand,
445 });
446 return;
447 }
448 _ => {}
449 }
450 }
451
452 ast::visit::visit_typed_call_arg(self, arg);
453 }
454}