⭐️ A friendly language for building type-safe, scalable systems!
at main 15 kB view raw
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}