Fork of daniellemaywood.uk/gleam — Wasm codegen work
70 kB
2334 lines
1mod constant;
2mod typed;
3mod untyped;
4
5#[cfg(test)]
6mod tests;
7pub mod visit;
8
9pub use self::typed::TypedExpr;
10pub use self::untyped::{FunctionLiteralKind, UntypedExpr, Use};
11
12pub use self::constant::{Constant, TypedConstant, UntypedConstant};
13
14use crate::analyse::Inferred;
15use crate::build::{Located, Target};
16use crate::parse::SpannedString;
17use crate::type_::expression::Implementations;
18use crate::type_::printer::Names;
19use crate::type_::{
20 self, Deprecation, ModuleValueConstructor, PatternConstructor, Type, ValueConstructor,
21};
22use std::sync::Arc;
23
24use ecow::EcoString;
25use num_bigint::BigInt;
26#[cfg(test)]
27use pretty_assertions::assert_eq;
28use vec1::Vec1;
29
30pub const TRY_VARIABLE: &str = "_try";
31pub const PIPE_VARIABLE: &str = "_pipe";
32pub const USE_ASSIGNMENT_VARIABLE: &str = "_use";
33pub const ASSERT_FAIL_VARIABLE: &str = "_assert_fail";
34pub const ASSERT_SUBJECT_VARIABLE: &str = "_assert_subject";
35pub const CAPTURE_VARIABLE: &str = "_capture";
36
37pub trait HasLocation {
38 fn location(&self) -> SrcSpan;
39}
40
41pub type TypedModule = Module<type_::ModuleInterface, TypedDefinition>;
42
43pub type UntypedModule = Module<(), TargetedDefinition>;
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Module<Info, Statements> {
47 pub name: EcoString,
48 pub documentation: Vec<EcoString>,
49 pub type_info: Info,
50 pub definitions: Vec<Statements>,
51 pub names: Names,
52}
53
54impl TypedModule {
55 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
56 self.definitions
57 .iter()
58 .find_map(|statement| statement.find_node(byte_index))
59 }
60}
61
62/// The `@target(erlang)` and `@target(javascript)` attributes can be used to
63/// mark a definition as only being for a specific target.
64///
65/// ```gleam
66/// const x: Int = 1
67///
68/// @target(erlang)
69/// pub fn main(a) { ...}
70/// ```
71///
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct TargetedDefinition {
74 pub definition: UntypedDefinition,
75 pub target: Option<Target>,
76}
77
78impl TargetedDefinition {
79 pub fn is_for(&self, target: Target) -> bool {
80 self.target.map(|t| t == target).unwrap_or(true)
81 }
82}
83
84impl UntypedModule {
85 pub fn dependencies(&self, target: Target) -> Vec<(EcoString, SrcSpan)> {
86 self.iter_statements(target)
87 .flat_map(|s| match s {
88 Definition::Import(Import {
89 module, location, ..
90 }) => Some((module.clone(), *location)),
91 _ => None,
92 })
93 .collect()
94 }
95
96 pub fn iter_statements(&self, target: Target) -> impl Iterator<Item = &UntypedDefinition> {
97 self.definitions
98 .iter()
99 .filter(move |def| def.is_for(target))
100 .map(|def| &def.definition)
101 }
102
103 pub fn into_iter_statements(self, target: Target) -> impl Iterator<Item = UntypedDefinition> {
104 self.definitions
105 .into_iter()
106 .filter(move |def| def.is_for(target))
107 .map(|def| def.definition)
108 }
109}
110
111#[test]
112fn module_dependencies_test() {
113 let parsed = crate::parse::parse_module(
114 camino::Utf8PathBuf::from("test/path"),
115 "import one
116 @target(erlang)
117 import two
118
119 @target(javascript)
120 import three
121
122 import four",
123 &crate::warning::WarningEmitter::null(),
124 )
125 .expect("syntax error");
126 let module = parsed.module;
127
128 assert_eq!(
129 vec![
130 ("one".into(), SrcSpan::new(0, 10)),
131 ("two".into(), SrcSpan::new(45, 55)),
132 ("four".into(), SrcSpan::new(118, 129)),
133 ],
134 module.dependencies(Target::Erlang)
135 );
136}
137
138pub type TypedArg = Arg<Arc<Type>>;
139pub type UntypedArg = Arg<()>;
140
141#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct Arg<T> {
143 pub names: ArgNames,
144 pub location: SrcSpan,
145 pub annotation: Option<TypeAst>,
146 pub type_: T,
147}
148
149impl<A> Arg<A> {
150 pub fn set_type<B>(self, t: B) -> Arg<B> {
151 Arg {
152 type_: t,
153 names: self.names,
154 location: self.location,
155 annotation: self.annotation,
156 }
157 }
158
159 pub fn get_variable_name(&self) -> Option<&EcoString> {
160 self.names.get_variable_name()
161 }
162
163 pub fn is_capture_hole(&self) -> bool {
164 match &self.names {
165 ArgNames::Named { name, .. } if name == CAPTURE_VARIABLE => true,
166 _ => false,
167 }
168 }
169}
170
171impl TypedArg {
172 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
173 if self.location.contains(byte_index) {
174 if let Some(annotation) = &self.annotation {
175 return annotation
176 .find_node(byte_index, self.type_.clone())
177 .or(Some(Located::Arg(self)));
178 }
179 Some(Located::Arg(self))
180 } else {
181 None
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub enum ArgNames {
188 Discard {
189 name: EcoString,
190 location: SrcSpan,
191 },
192 LabelledDiscard {
193 label: EcoString,
194 label_location: SrcSpan,
195 name: EcoString,
196 name_location: SrcSpan,
197 },
198 Named {
199 name: EcoString,
200 location: SrcSpan,
201 },
202 NamedLabelled {
203 label: EcoString,
204 label_location: SrcSpan,
205 name: EcoString,
206 name_location: SrcSpan,
207 },
208}
209
210impl ArgNames {
211 pub fn get_label(&self) -> Option<&EcoString> {
212 match self {
213 ArgNames::Discard { .. } | ArgNames::Named { .. } => None,
214 ArgNames::LabelledDiscard { label, .. } | ArgNames::NamedLabelled { label, .. } => {
215 Some(label)
216 }
217 }
218 }
219 pub fn get_variable_name(&self) -> Option<&EcoString> {
220 match self {
221 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None,
222 ArgNames::NamedLabelled { name, .. } | ArgNames::Named { name, .. } => Some(name),
223 }
224 }
225}
226
227pub type TypedRecordConstructor = RecordConstructor<Arc<Type>>;
228
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct RecordConstructor<T> {
231 pub location: SrcSpan,
232 pub name_location: SrcSpan,
233 pub name: EcoString,
234 pub arguments: Vec<RecordConstructorArg<T>>,
235 pub documentation: Option<(u32, EcoString)>,
236}
237
238impl<A> RecordConstructor<A> {
239 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
240 self.documentation = Some(new_doc);
241 }
242}
243
244pub type TypedRecordConstructorArg = RecordConstructorArg<Arc<Type>>;
245
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct RecordConstructorArg<T> {
248 pub label: Option<SpannedString>,
249 pub ast: TypeAst,
250 pub location: SrcSpan,
251 pub type_: T,
252 pub doc: Option<(u32, EcoString)>,
253}
254
255impl<T: PartialEq> RecordConstructorArg<T> {
256 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
257 self.doc = Some(new_doc);
258 }
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct TypeAstConstructor {
263 pub location: SrcSpan,
264 pub module: Option<(EcoString, SrcSpan)>,
265 pub name: EcoString,
266 pub arguments: Vec<TypeAst>,
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270pub struct TypeAstFn {
271 pub location: SrcSpan,
272 pub arguments: Vec<TypeAst>,
273 pub return_: Box<TypeAst>,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct TypeAstVar {
278 pub location: SrcSpan,
279 pub name: EcoString,
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
283pub struct TypeAstTuple {
284 pub location: SrcSpan,
285 pub elems: Vec<TypeAst>,
286}
287
288#[derive(Debug, Clone, PartialEq, Eq)]
289pub struct TypeAstHole {
290 pub location: SrcSpan,
291 pub name: EcoString,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub enum TypeAst {
296 Constructor(TypeAstConstructor),
297 Fn(TypeAstFn),
298 Var(TypeAstVar),
299 Tuple(TypeAstTuple),
300 Hole(TypeAstHole),
301}
302
303impl TypeAst {
304 pub fn location(&self) -> SrcSpan {
305 match self {
306 TypeAst::Fn(TypeAstFn { location, .. })
307 | TypeAst::Var(TypeAstVar { location, .. })
308 | TypeAst::Hole(TypeAstHole { location, .. })
309 | TypeAst::Tuple(TypeAstTuple { location, .. })
310 | TypeAst::Constructor(TypeAstConstructor { location, .. }) => *location,
311 }
312 }
313
314 pub fn is_logically_equal(&self, other: &TypeAst) -> bool {
315 match self {
316 TypeAst::Constructor(TypeAstConstructor {
317 module,
318 name,
319 arguments,
320 location: _,
321 }) => match other {
322 TypeAst::Constructor(TypeAstConstructor {
323 module: o_module,
324 name: o_name,
325 arguments: o_arguments,
326 location: _,
327 }) => {
328 let module_name =
329 |m: &Option<(EcoString, _)>| m.as_ref().map(|(m, _)| m.clone());
330 module_name(module) == module_name(o_module)
331 && name == o_name
332 && arguments.len() == o_arguments.len()
333 && arguments
334 .iter()
335 .zip(o_arguments)
336 .all(|a| a.0.is_logically_equal(a.1))
337 }
338 _ => false,
339 },
340 TypeAst::Fn(TypeAstFn {
341 arguments,
342 return_,
343 location: _,
344 }) => match other {
345 TypeAst::Fn(TypeAstFn {
346 arguments: o_arguments,
347 return_: o_return_,
348 location: _,
349 }) => {
350 arguments.len() == o_arguments.len()
351 && arguments
352 .iter()
353 .zip(o_arguments)
354 .all(|a| a.0.is_logically_equal(a.1))
355 && return_.is_logically_equal(o_return_)
356 }
357 _ => false,
358 },
359 TypeAst::Var(TypeAstVar { name, location: _ }) => match other {
360 TypeAst::Var(TypeAstVar {
361 name: o_name,
362 location: _,
363 }) => name == o_name,
364 _ => false,
365 },
366 TypeAst::Tuple(TypeAstTuple { elems, location: _ }) => match other {
367 TypeAst::Tuple(TypeAstTuple {
368 elems: o_elems,
369 location: _,
370 }) => {
371 elems.len() == o_elems.len()
372 && elems
373 .iter()
374 .zip(o_elems)
375 .all(|a| a.0.is_logically_equal(a.1))
376 }
377 _ => false,
378 },
379 TypeAst::Hole(TypeAstHole { name, location: _ }) => match other {
380 TypeAst::Hole(TypeAstHole {
381 name: o_name,
382 location: _,
383 }) => name == o_name,
384 _ => false,
385 },
386 }
387 }
388
389 pub fn find_node(&self, byte_index: u32, type_: Arc<Type>) -> Option<Located<'_>> {
390 if !self.location().contains(byte_index) {
391 return None;
392 }
393
394 match self {
395 TypeAst::Fn(TypeAstFn {
396 arguments, return_, ..
397 }) => type_
398 .fn_types()
399 .and_then(|(arg_types, ret_type)| {
400 if let Some(arg) = arguments
401 .iter()
402 .zip(arg_types)
403 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
404 {
405 return Some(arg);
406 }
407 if let Some(ret) = return_.find_node(byte_index, ret_type) {
408 return Some(ret);
409 }
410
411 None
412 })
413 .or(Some(Located::Annotation(self.location(), type_))),
414 TypeAst::Constructor(TypeAstConstructor { arguments, .. }) => type_
415 .constructor_types()
416 .and_then(|arg_types| {
417 if let Some(arg) = arguments
418 .iter()
419 .zip(arg_types)
420 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
421 {
422 return Some(arg);
423 }
424
425 None
426 })
427 .or(Some(Located::Annotation(self.location(), type_))),
428 TypeAst::Tuple(TypeAstTuple { elems, .. }) => type_
429 .tuple_types()
430 .and_then(|elem_types| {
431 if let Some(e) = elems
432 .iter()
433 .zip(elem_types)
434 .find_map(|(e, e_type)| e.find_node(byte_index, e_type.clone()))
435 {
436 return Some(e);
437 }
438
439 None
440 })
441 .or(Some(Located::Annotation(self.location(), type_))),
442 TypeAst::Var(_) | TypeAst::Hole(_) => Some(Located::Annotation(self.location(), type_)),
443 }
444 }
445
446 /// Generates an annotation corresponding to the type.
447 pub fn print(&self, buffer: &mut EcoString) {
448 match &self {
449 TypeAst::Var(var) => buffer.push_str(&var.name),
450 TypeAst::Hole(hole) => buffer.push_str(&hole.name),
451 TypeAst::Tuple(tuple) => {
452 buffer.push_str("#(");
453 for (i, elem) in tuple.elems.iter().enumerate() {
454 elem.print(buffer);
455 if i < tuple.elems.len() - 1 {
456 buffer.push_str(", ");
457 }
458 }
459 buffer.push(')')
460 }
461 TypeAst::Fn(func) => {
462 buffer.push_str("fn(");
463 for (i, argument) in func.arguments.iter().enumerate() {
464 argument.print(buffer);
465 if i < func.arguments.len() - 1 {
466 buffer.push_str(", ");
467 }
468 }
469 buffer.push(')');
470 buffer.push_str(" -> ");
471 func.return_.print(buffer);
472 }
473 TypeAst::Constructor(constructor) => {
474 if let Some((module, _)) = &constructor.module {
475 buffer.push_str(module);
476 buffer.push('.');
477 }
478 buffer.push_str(&constructor.name);
479 if !constructor.arguments.is_empty() {
480 buffer.push('(');
481 for (i, argument) in constructor.arguments.iter().enumerate() {
482 argument.print(buffer);
483 if i < constructor.arguments.len() - 1 {
484 buffer.push_str(", ");
485 }
486 }
487 buffer.push(')');
488 }
489 }
490 }
491 }
492}
493
494#[test]
495fn type_ast_print_fn() {
496 let mut buffer = EcoString::new();
497 let ast = TypeAst::Fn(TypeAstFn {
498 location: SrcSpan { start: 1, end: 1 },
499 arguments: vec![
500 TypeAst::Var(TypeAstVar {
501 location: SrcSpan { start: 1, end: 1 },
502 name: "String".into(),
503 }),
504 TypeAst::Var(TypeAstVar {
505 location: SrcSpan { start: 1, end: 1 },
506 name: "Bool".into(),
507 }),
508 ],
509 return_: Box::new(TypeAst::Var(TypeAstVar {
510 location: SrcSpan { start: 1, end: 1 },
511 name: "Int".into(),
512 })),
513 });
514 ast.print(&mut buffer);
515 assert_eq!(&buffer, "fn(String, Bool) -> Int")
516}
517
518#[test]
519fn type_ast_print_constructor() {
520 let mut buffer = EcoString::new();
521 let ast = TypeAst::Constructor(TypeAstConstructor {
522 name: "SomeType".into(),
523 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
524 location: SrcSpan { start: 1, end: 1 },
525 arguments: vec![
526 TypeAst::Var(TypeAstVar {
527 location: SrcSpan { start: 1, end: 1 },
528 name: "String".into(),
529 }),
530 TypeAst::Var(TypeAstVar {
531 location: SrcSpan { start: 1, end: 1 },
532 name: "Bool".into(),
533 }),
534 ],
535 });
536 ast.print(&mut buffer);
537 assert_eq!(&buffer, "some_module.SomeType(String, Bool)")
538}
539
540#[test]
541fn type_ast_print_tuple() {
542 let mut buffer = EcoString::new();
543 let ast = TypeAst::Tuple(TypeAstTuple {
544 location: SrcSpan { start: 1, end: 1 },
545 elems: vec![
546 TypeAst::Constructor(TypeAstConstructor {
547 name: "SomeType".into(),
548 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
549 location: SrcSpan { start: 1, end: 1 },
550 arguments: vec![
551 TypeAst::Var(TypeAstVar {
552 location: SrcSpan { start: 1, end: 1 },
553 name: "String".into(),
554 }),
555 TypeAst::Var(TypeAstVar {
556 location: SrcSpan { start: 1, end: 1 },
557 name: "Bool".into(),
558 }),
559 ],
560 }),
561 TypeAst::Fn(TypeAstFn {
562 location: SrcSpan { start: 1, end: 1 },
563 arguments: vec![
564 TypeAst::Var(TypeAstVar {
565 location: SrcSpan { start: 1, end: 1 },
566 name: "String".into(),
567 }),
568 TypeAst::Var(TypeAstVar {
569 location: SrcSpan { start: 1, end: 1 },
570 name: "Bool".into(),
571 }),
572 ],
573 return_: Box::new(TypeAst::Var(TypeAstVar {
574 location: SrcSpan { start: 1, end: 1 },
575 name: "Int".into(),
576 })),
577 }),
578 ],
579 });
580 ast.print(&mut buffer);
581 assert_eq!(
582 &buffer,
583 "#(some_module.SomeType(String, Bool), fn(String, Bool) -> Int)"
584 )
585}
586
587#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
588pub enum Publicity {
589 Public,
590 Private,
591 Internal { attribute_location: Option<SrcSpan> },
592}
593
594impl Publicity {
595 pub fn is_private(&self) -> bool {
596 match self {
597 Self::Private => true,
598 Self::Public | Self::Internal { .. } => false,
599 }
600 }
601
602 pub fn is_internal(&self) -> bool {
603 match self {
604 Self::Internal { .. } => true,
605 Self::Public | Self::Private => false,
606 }
607 }
608
609 pub fn is_public(&self) -> bool {
610 match self {
611 Self::Public => true,
612 Self::Internal { .. } | Self::Private => false,
613 }
614 }
615
616 pub fn is_importable(&self) -> bool {
617 match self {
618 Self::Internal { .. } | Self::Public => true,
619 Self::Private => false,
620 }
621 }
622}
623
624#[derive(Debug, Clone, PartialEq, Eq)]
625/// A function definition
626///
627/// Note that an anonymous function will have `None` as the name field, while a
628/// named function will have `Some`.
629///
630/// # Example(s)
631///
632/// ```gleam
633/// // Public function
634/// pub fn wobble() -> String { ... }
635/// // Private function
636/// fn wibble(x: Int) -> Int { ... }
637/// // Anonymous function
638/// fn(x: Int) { ... }
639/// ```
640pub struct Function<T, Expr> {
641 pub location: SrcSpan,
642 pub end_position: u32,
643 pub name: Option<SpannedString>,
644 pub arguments: Vec<Arg<T>>,
645 pub body: Vec1<Statement<T, Expr>>,
646 pub publicity: Publicity,
647 pub deprecation: Deprecation,
648 pub return_annotation: Option<TypeAst>,
649 pub return_type: T,
650 pub documentation: Option<(u32, EcoString)>,
651 pub external_erlang: Option<(EcoString, EcoString, SrcSpan)>,
652 pub external_javascript: Option<(EcoString, EcoString, SrcSpan)>,
653 pub implementations: Implementations,
654}
655
656pub type TypedFunction = Function<Arc<Type>, TypedExpr>;
657pub type UntypedFunction = Function<(), UntypedExpr>;
658
659impl<T, E> Function<T, E> {
660 pub fn full_location(&self) -> SrcSpan {
661 SrcSpan::new(self.location.start, self.end_position)
662 }
663}
664
665pub type UntypedImport = Import<()>;
666
667#[derive(Debug, Clone, PartialEq, Eq)]
668/// Import another Gleam module so the current module can use the types and
669/// values it defines.
670///
671/// # Example(s)
672///
673/// ```gleam
674/// import unix/cat
675/// // Import with alias
676/// import animal/cat as kitty
677/// ```
678pub struct Import<PackageName> {
679 pub documentation: Option<EcoString>,
680 pub location: SrcSpan,
681 pub module: EcoString,
682 pub as_name: Option<(AssignName, SrcSpan)>,
683 pub unqualified_values: Vec<UnqualifiedImport>,
684 pub unqualified_types: Vec<UnqualifiedImport>,
685 pub package: PackageName,
686}
687
688impl<T> Import<T> {
689 pub(crate) fn used_name(&self) -> Option<EcoString> {
690 match self.as_name.as_ref() {
691 Some((AssignName::Variable(name), _)) => Some(name.clone()),
692 Some((AssignName::Discard(_), _)) => None,
693 None => self.module.split('/').last().map(EcoString::from),
694 }
695 }
696
697 pub(crate) fn alias_location(&self) -> Option<SrcSpan> {
698 self.as_name.as_ref().map(|(_, location)| *location)
699 }
700}
701
702pub type UntypedModuleConstant = ModuleConstant<(), ()>;
703pub type TypedModuleConstant = ModuleConstant<Arc<Type>, EcoString>;
704
705#[derive(Debug, Clone, PartialEq, Eq)]
706/// A certain fixed value that can be used in multiple places
707///
708/// # Example(s)
709///
710/// ```gleam
711/// pub const start_year = 2101
712/// pub const end_year = 2111
713/// ```
714pub struct ModuleConstant<T, ConstantRecordTag> {
715 pub documentation: Option<(u32, EcoString)>,
716 /// The location of the constant, starting at the "(pub) const" keywords and
717 /// ending after the ": Type" annotation, or (without an annotation) after its name.
718 pub location: SrcSpan,
719 pub publicity: Publicity,
720 pub name: EcoString,
721 pub name_location: SrcSpan,
722 pub annotation: Option<TypeAst>,
723 pub value: Box<Constant<T, ConstantRecordTag>>,
724 pub type_: T,
725 pub deprecation: Deprecation,
726 pub implementations: Implementations,
727}
728
729pub type UntypedCustomType = CustomType<()>;
730
731#[derive(Debug, Clone, PartialEq, Eq)]
732/// A newly defined type with one or more constructors.
733/// Each variant of the custom type can contain different types, so the type is
734/// the product of the types contained by each variant.
735///
736/// This might be called an algebraic data type (ADT) or tagged union in other
737/// languages and type systems.
738///
739///
740/// # Example(s)
741///
742/// ```gleam
743/// pub type Cat {
744/// Cat(name: String, cuteness: Int)
745/// }
746/// ```
747pub struct CustomType<T> {
748 pub location: SrcSpan,
749 pub end_position: u32,
750 pub name: EcoString,
751 pub name_location: SrcSpan,
752 pub publicity: Publicity,
753 pub constructors: Vec<RecordConstructor<T>>,
754 pub documentation: Option<(u32, EcoString)>,
755 pub deprecation: Deprecation,
756 pub opaque: bool,
757 /// The names of the type parameters.
758 pub parameters: Vec<SpannedString>,
759 /// Once type checked this field will contain the type information for the
760 /// type parameters.
761 pub typed_parameters: Vec<T>,
762}
763
764impl<T> CustomType<T> {
765 /// The `location` field of a `CustomType` is only the location of `pub type
766 /// TheName`. This method returns a `SrcSpan` that includes the entire type
767 /// definition.
768 pub fn full_location(&self) -> SrcSpan {
769 SrcSpan::new(self.location.start, self.end_position)
770 }
771}
772
773pub type UntypedTypeAlias = TypeAlias<()>;
774
775#[derive(Debug, Clone, PartialEq, Eq)]
776/// A new name for an existing type
777///
778/// # Example(s)
779///
780/// ```gleam
781/// pub type Headers =
782/// List(#(String, String))
783/// ```
784pub struct TypeAlias<T> {
785 pub location: SrcSpan,
786 pub alias: EcoString,
787 pub name_location: SrcSpan,
788 pub parameters: Vec<SpannedString>,
789 pub type_ast: TypeAst,
790 pub type_: T,
791 pub publicity: Publicity,
792 pub documentation: Option<(u32, EcoString)>,
793 pub deprecation: Deprecation,
794}
795
796pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, EcoString, EcoString>;
797pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
798
799#[derive(Debug, Clone, PartialEq, Eq)]
800pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
801 Function(Function<T, Expr>),
802
803 TypeAlias(TypeAlias<T>),
804
805 CustomType(CustomType<T>),
806
807 Import(Import<PackageName>),
808
809 ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
810}
811
812impl TypedDefinition {
813 pub fn main_function(&self) -> Option<&TypedFunction> {
814 match self {
815 Definition::Function(f) if f.name.as_ref().is_some_and(|(_, name)| name == "main") => {
816 Some(f)
817 }
818 _ => None,
819 }
820 }
821
822 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
823 match self {
824 Definition::Function(function) => {
825 // Search for the corresponding node inside the function
826 // only if the index falls within the function's full location.
827 if !function.full_location().contains(byte_index) {
828 return None;
829 }
830
831 if let Some(found) = function.body.iter().find_map(|s| s.find_node(byte_index)) {
832 return Some(found);
833 }
834
835 if let Some(found_arg) = function
836 .arguments
837 .iter()
838 .find_map(|arg| arg.find_node(byte_index))
839 {
840 return Some(found_arg);
841 };
842
843 if let Some(found_statement) = function
844 .body
845 .iter()
846 .find(|statement| statement.location().contains(byte_index))
847 {
848 return Some(Located::Statement(found_statement));
849 };
850
851 // Check if location is within the return annotation.
852 if let Some(l) = function
853 .return_annotation
854 .iter()
855 .find_map(|a| a.find_node(byte_index, function.return_type.clone()))
856 {
857 return Some(l);
858 };
859
860 // Note that the fn `.location` covers the function head, not
861 // the entire statement.
862 if function.location.contains(byte_index) {
863 Some(Located::ModuleStatement(self))
864 } else if function.full_location().contains(byte_index) {
865 Some(Located::FunctionBody(function))
866 } else {
867 None
868 }
869 }
870
871 Definition::CustomType(custom) => {
872 // Check if location is within the type of one of the arguments of a constructor.
873 if let Some(annotation) = custom
874 .constructors
875 .iter()
876 .find(|constructor| constructor.location.contains(byte_index))
877 .and_then(|constructor| {
878 constructor
879 .arguments
880 .iter()
881 .find(|arg| arg.location.contains(byte_index))
882 })
883 .filter(|arg| arg.location.contains(byte_index))
884 .and_then(|arg| arg.ast.find_node(byte_index, arg.type_.clone()))
885 {
886 return Some(annotation);
887 }
888
889 // Note that the custom type `.location` covers the function
890 // head, not the entire statement.
891 if custom.full_location().contains(byte_index) {
892 Some(Located::ModuleStatement(self))
893 } else {
894 None
895 }
896 }
897
898 Definition::TypeAlias(alias) => {
899 // Check if location is within the type being aliased.
900 if let Some(l) = alias.type_ast.find_node(byte_index, alias.type_.clone()) {
901 return Some(l);
902 }
903
904 if alias.location.contains(byte_index) {
905 Some(Located::ModuleStatement(self))
906 } else {
907 None
908 }
909 }
910
911 Definition::ModuleConstant(constant) => {
912 // Check if location is within the annotation.
913 if let Some(annotation) = &constant.annotation {
914 if let Some(l) = annotation.find_node(byte_index, constant.type_.clone()) {
915 return Some(l);
916 }
917 }
918
919 if constant.location.contains(byte_index) {
920 Some(Located::ModuleStatement(self))
921 } else {
922 None
923 }
924 }
925
926 Definition::Import(import) => {
927 if self.location().contains(byte_index) {
928 if let Some(unqualified) = import
929 .unqualified_values
930 .iter()
931 .find(|i| i.location.contains(byte_index))
932 {
933 return Some(Located::UnqualifiedImport(
934 crate::build::UnqualifiedImport {
935 name: &unqualified.name,
936 module: &import.module,
937 is_type: false,
938 location: &unqualified.location,
939 },
940 ));
941 }
942
943 if let Some(unqualified) = import
944 .unqualified_types
945 .iter()
946 .find(|i| i.location.contains(byte_index))
947 {
948 return Some(Located::UnqualifiedImport(
949 crate::build::UnqualifiedImport {
950 name: &unqualified.name,
951 module: &import.module,
952 is_type: true,
953 location: &unqualified.location,
954 },
955 ));
956 }
957
958 Some(Located::ModuleStatement(self))
959 } else {
960 None
961 }
962 }
963 }
964 }
965}
966
967impl<A, B, C, E> Definition<A, B, C, E> {
968 pub fn location(&self) -> SrcSpan {
969 match self {
970 Definition::Function(Function { location, .. })
971 | Definition::Import(Import { location, .. })
972 | Definition::TypeAlias(TypeAlias { location, .. })
973 | Definition::CustomType(CustomType { location, .. })
974 | Definition::ModuleConstant(ModuleConstant { location, .. }) => *location,
975 }
976 }
977
978 /// Returns `true` if the definition is [`Import`].
979 ///
980 /// [`Import`]: Definition::Import
981 #[must_use]
982 pub fn is_import(&self) -> bool {
983 matches!(self, Self::Import(..))
984 }
985
986 /// Returns `true` if the module statement is [`Function`].
987 ///
988 /// [`Function`]: ModuleStatement::Function
989 #[must_use]
990 pub fn is_function(&self) -> bool {
991 matches!(self, Self::Function(..))
992 }
993
994 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
995 match self {
996 Definition::Import(Import { .. }) => (),
997
998 Definition::Function(Function { documentation, .. })
999 | Definition::TypeAlias(TypeAlias { documentation, .. })
1000 | Definition::CustomType(CustomType { documentation, .. })
1001 | Definition::ModuleConstant(ModuleConstant { documentation, .. }) => {
1002 let _ = std::mem::replace(documentation, Some(new_doc));
1003 }
1004 }
1005 }
1006
1007 pub fn get_doc(&self) -> Option<EcoString> {
1008 match self {
1009 Definition::Import(Import { .. }) => None,
1010
1011 Definition::Function(Function {
1012 documentation: doc, ..
1013 })
1014 | Definition::TypeAlias(TypeAlias {
1015 documentation: doc, ..
1016 })
1017 | Definition::CustomType(CustomType {
1018 documentation: doc, ..
1019 })
1020 | Definition::ModuleConstant(ModuleConstant {
1021 documentation: doc, ..
1022 }) => doc.as_ref().map(|(_, doc)| doc.clone()),
1023 }
1024 }
1025
1026 pub fn is_internal(&self) -> bool {
1027 match self {
1028 Definition::Function(Function { publicity, .. })
1029 | Definition::CustomType(CustomType { publicity, .. })
1030 | Definition::ModuleConstant(ModuleConstant { publicity, .. })
1031 | Definition::TypeAlias(TypeAlias { publicity, .. }) => publicity.is_internal(),
1032
1033 Definition::Import(_) => false,
1034 }
1035 }
1036}
1037
1038#[derive(Debug, Clone, PartialEq, Eq)]
1039pub struct UnqualifiedImport {
1040 pub location: SrcSpan,
1041 pub name: EcoString,
1042 pub as_name: Option<EcoString>,
1043}
1044
1045impl UnqualifiedImport {
1046 pub fn used_name(&self) -> &EcoString {
1047 self.as_name.as_ref().unwrap_or(&self.name)
1048 }
1049}
1050
1051#[derive(Debug, Clone, PartialEq, Eq, Copy, Default, serde::Serialize, serde::Deserialize)]
1052pub enum Layer {
1053 #[default]
1054 Value,
1055 Type,
1056}
1057
1058impl Layer {
1059 /// Returns `true` if the layer is [`Value`].
1060 pub fn is_value(&self) -> bool {
1061 matches!(self, Self::Value)
1062 }
1063}
1064
1065#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1066pub enum BinOp {
1067 // Boolean logic
1068 And,
1069 Or,
1070
1071 // Equality
1072 Eq,
1073 NotEq,
1074
1075 // Order comparison
1076 LtInt,
1077 LtEqInt,
1078 LtFloat,
1079 LtEqFloat,
1080 GtEqInt,
1081 GtInt,
1082 GtEqFloat,
1083 GtFloat,
1084
1085 // Maths
1086 AddInt,
1087 AddFloat,
1088 SubInt,
1089 SubFloat,
1090 MultInt,
1091 MultFloat,
1092 DivInt,
1093 DivFloat,
1094 RemainderInt,
1095
1096 // Strings
1097 Concatenate,
1098}
1099
1100#[derive(Clone, Copy, Debug, PartialEq)]
1101pub enum OperatorKind {
1102 BooleanLogic,
1103 Equality,
1104 IntComparison,
1105 FLoatComparison,
1106 IntMath,
1107 FloatMath,
1108 StringConcatenation,
1109}
1110
1111impl BinOp {
1112 pub fn precedence(&self) -> u8 {
1113 // Ensure that this matches the other precedence function for guards
1114 match self {
1115 Self::Or => 1,
1116
1117 Self::And => 2,
1118
1119 Self::Eq | Self::NotEq => 3,
1120
1121 Self::LtInt
1122 | Self::LtEqInt
1123 | Self::LtFloat
1124 | Self::LtEqFloat
1125 | Self::GtEqInt
1126 | Self::GtInt
1127 | Self::GtEqFloat
1128 | Self::GtFloat => 4,
1129
1130 Self::Concatenate => 5,
1131
1132 // Pipe is 6
1133 Self::AddInt | Self::AddFloat | Self::SubInt | Self::SubFloat => 7,
1134
1135 Self::MultInt
1136 | Self::MultFloat
1137 | Self::DivInt
1138 | Self::DivFloat
1139 | Self::RemainderInt => 8,
1140 }
1141 }
1142
1143 pub fn name(&self) -> &'static str {
1144 match self {
1145 Self::And => "&&",
1146 Self::Or => "||",
1147 Self::LtInt => "<",
1148 Self::LtEqInt => "<=",
1149 Self::LtFloat => "<.",
1150 Self::LtEqFloat => "<=.",
1151 Self::Eq => "==",
1152 Self::NotEq => "!=",
1153 Self::GtEqInt => ">=",
1154 Self::GtInt => ">",
1155 Self::GtEqFloat => ">=.",
1156 Self::GtFloat => ">.",
1157 Self::AddInt => "+",
1158 Self::AddFloat => "+.",
1159 Self::SubInt => "-",
1160 Self::SubFloat => "-.",
1161 Self::MultInt => "*",
1162 Self::MultFloat => "*.",
1163 Self::DivInt => "/",
1164 Self::DivFloat => "/.",
1165 Self::RemainderInt => "%",
1166 Self::Concatenate => "<>",
1167 }
1168 }
1169
1170 pub fn operator_kind(&self) -> OperatorKind {
1171 match self {
1172 Self::Concatenate => OperatorKind::StringConcatenation,
1173 Self::Eq | Self::NotEq => OperatorKind::Equality,
1174 Self::And | Self::Or => OperatorKind::BooleanLogic,
1175 Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => {
1176 OperatorKind::IntComparison
1177 }
1178 Self::LtFloat | Self::LtEqFloat | Self::GtEqFloat | Self::GtFloat => {
1179 OperatorKind::FLoatComparison
1180 }
1181 Self::AddInt | Self::SubInt | Self::MultInt | Self::RemainderInt | Self::DivInt => {
1182 OperatorKind::IntMath
1183 }
1184 Self::AddFloat | Self::SubFloat | Self::MultFloat | Self::DivFloat => {
1185 OperatorKind::FloatMath
1186 }
1187 }
1188 }
1189
1190 pub fn can_be_grouped_with(&self, other: &BinOp) -> bool {
1191 self.operator_kind() == other.operator_kind()
1192 }
1193}
1194
1195#[derive(Debug, PartialEq, Eq, Clone)]
1196pub struct CallArg<A> {
1197 pub label: Option<EcoString>,
1198 pub location: SrcSpan,
1199 pub value: A,
1200 pub implicit: Option<ImplicitCallArgOrigin>,
1201}
1202
1203#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1204pub enum ImplicitCallArgOrigin {
1205 /// The implicit callback argument passed as the last argument to the
1206 /// function on the right hand side of `use`.
1207 ///
1208 Use,
1209 /// An argument added by the compiler when rewriting a pipe `left |> right`.
1210 ///
1211 Pipe,
1212 /// An argument added by the compiler to fill in all the missing fields of a
1213 /// record that are being ignored with the `..` syntax.
1214 ///
1215 PatternFieldSpread,
1216 /// An argument used to fill in the missing args when a function on the
1217 /// right hand side of `use` is being called with the wrong arity.
1218 ///
1219 IncorrectArityUse,
1220}
1221
1222impl<A> CallArg<A> {
1223 #[must_use]
1224 pub fn is_implicit(&self) -> bool {
1225 self.implicit.is_some()
1226 }
1227}
1228
1229impl CallArg<TypedExpr> {
1230 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1231 match (self.implicit, &self.value) {
1232 // If a call argument is the implicit use callback then we don't
1233 // want to look at its arguments and body but we don't want to
1234 // return the whole anonymous function if anything else doesn't
1235 // match.
1236 //
1237 // In addition, if the callback is invalid because it couldn't be
1238 // typed, we don't want to return it as it would make it hard for
1239 // the LSP to give any suggestions on the use function being typed.
1240 //
1241 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Invalid { .. }) => None,
1242 // So the code below is exactly the same as
1243 // `TypedExpr::Fn{}.find_node()` except we do not return self as a
1244 // fallback.
1245 //
1246 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Fn { args, body, .. }) => args
1247 .iter()
1248 .find_map(|arg| arg.find_node(byte_index))
1249 .or_else(|| body.iter().find_map(|s| s.find_node(byte_index))),
1250 // In all other cases we're happy with the default behaviour.
1251 //
1252 _ => self.value.find_node(byte_index),
1253 }
1254 }
1255}
1256
1257impl CallArg<TypedPattern> {
1258 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1259 self.value.find_node(byte_index)
1260 }
1261}
1262
1263impl CallArg<UntypedExpr> {
1264 pub fn is_capture_hole(&self) -> bool {
1265 match &self.value {
1266 UntypedExpr::Var { ref name, .. } => name == CAPTURE_VARIABLE,
1267 _ => false,
1268 }
1269 }
1270}
1271
1272impl<T> CallArg<T>
1273where
1274 T: HasLocation,
1275{
1276 #[must_use]
1277 pub fn uses_label_shorthand(&self) -> bool {
1278 self.label.is_some() && self.location == self.value.location()
1279 }
1280}
1281
1282impl<T> HasLocation for CallArg<T> {
1283 fn location(&self) -> SrcSpan {
1284 self.location
1285 }
1286}
1287
1288#[derive(Debug, Clone, PartialEq, Eq)]
1289pub struct RecordBeingUpdated {
1290 pub base: Box<UntypedExpr>,
1291 pub location: SrcSpan,
1292}
1293
1294#[derive(Debug, Clone, PartialEq, Eq)]
1295pub struct UntypedRecordUpdateArg {
1296 pub label: EcoString,
1297 pub location: SrcSpan,
1298 pub value: UntypedExpr,
1299}
1300
1301impl UntypedRecordUpdateArg {
1302 #[must_use]
1303 pub fn uses_label_shorthand(&self) -> bool {
1304 self.value.location() == self.location
1305 }
1306}
1307
1308#[derive(Debug, Clone, PartialEq, Eq)]
1309pub struct TypedRecordUpdateArg {
1310 pub label: EcoString,
1311 pub location: SrcSpan,
1312 pub value: TypedExpr,
1313 pub index: u32,
1314}
1315
1316impl TypedRecordUpdateArg {
1317 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1318 self.value.find_node(byte_index)
1319 }
1320
1321 #[must_use]
1322 pub fn uses_label_shorthand(&self) -> bool {
1323 self.value.location() == self.location
1324 }
1325}
1326
1327pub type MultiPattern<Type> = Vec<Pattern<Type>>;
1328
1329pub type UntypedMultiPattern = MultiPattern<()>;
1330pub type TypedMultiPattern = MultiPattern<Arc<Type>>;
1331
1332pub type TypedClause = Clause<TypedExpr, Arc<Type>, EcoString>;
1333
1334pub type UntypedClause = Clause<UntypedExpr, (), ()>;
1335
1336#[derive(Debug, Clone, PartialEq, Eq)]
1337pub struct Clause<Expr, Type, RecordTag> {
1338 pub location: SrcSpan,
1339 pub pattern: MultiPattern<Type>,
1340 pub alternative_patterns: Vec<MultiPattern<Type>>,
1341 pub guard: Option<ClauseGuard<Type, RecordTag>>,
1342 pub then: Expr,
1343}
1344
1345impl<A, B, C> Clause<A, B, C> {
1346 pub fn pattern_count(&self) -> usize {
1347 1 + self.alternative_patterns.len()
1348 }
1349}
1350
1351impl TypedClause {
1352 pub fn location(&self) -> SrcSpan {
1353 SrcSpan {
1354 start: self
1355 .pattern
1356 .first()
1357 .map(|p| p.location().start)
1358 .unwrap_or_default(),
1359 end: self.then.location().end,
1360 }
1361 }
1362
1363 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1364 self.pattern
1365 .iter()
1366 .find_map(|p| p.find_node(byte_index))
1367 .or_else(|| self.then.find_node(byte_index))
1368 }
1369}
1370
1371pub type UntypedClauseGuard = ClauseGuard<(), ()>;
1372pub type TypedClauseGuard = ClauseGuard<Arc<Type>, EcoString>;
1373
1374#[derive(Debug, Clone, PartialEq, Eq)]
1375pub enum ClauseGuard<Type, RecordTag> {
1376 Equals {
1377 location: SrcSpan,
1378 left: Box<Self>,
1379 right: Box<Self>,
1380 },
1381
1382 NotEquals {
1383 location: SrcSpan,
1384 left: Box<Self>,
1385 right: Box<Self>,
1386 },
1387
1388 GtInt {
1389 location: SrcSpan,
1390 left: Box<Self>,
1391 right: Box<Self>,
1392 },
1393
1394 GtEqInt {
1395 location: SrcSpan,
1396 left: Box<Self>,
1397 right: Box<Self>,
1398 },
1399
1400 LtInt {
1401 location: SrcSpan,
1402 left: Box<Self>,
1403 right: Box<Self>,
1404 },
1405
1406 LtEqInt {
1407 location: SrcSpan,
1408 left: Box<Self>,
1409 right: Box<Self>,
1410 },
1411
1412 GtFloat {
1413 location: SrcSpan,
1414 left: Box<Self>,
1415 right: Box<Self>,
1416 },
1417
1418 GtEqFloat {
1419 location: SrcSpan,
1420 left: Box<Self>,
1421 right: Box<Self>,
1422 },
1423
1424 LtFloat {
1425 location: SrcSpan,
1426 left: Box<Self>,
1427 right: Box<Self>,
1428 },
1429
1430 LtEqFloat {
1431 location: SrcSpan,
1432 left: Box<Self>,
1433 right: Box<Self>,
1434 },
1435
1436 AddInt {
1437 location: SrcSpan,
1438 left: Box<Self>,
1439 right: Box<Self>,
1440 },
1441
1442 AddFloat {
1443 location: SrcSpan,
1444 left: Box<Self>,
1445 right: Box<Self>,
1446 },
1447
1448 SubInt {
1449 location: SrcSpan,
1450 left: Box<Self>,
1451 right: Box<Self>,
1452 },
1453
1454 SubFloat {
1455 location: SrcSpan,
1456 left: Box<Self>,
1457 right: Box<Self>,
1458 },
1459
1460 MultInt {
1461 location: SrcSpan,
1462 left: Box<Self>,
1463 right: Box<Self>,
1464 },
1465
1466 MultFloat {
1467 location: SrcSpan,
1468 left: Box<Self>,
1469 right: Box<Self>,
1470 },
1471
1472 DivInt {
1473 location: SrcSpan,
1474 left: Box<Self>,
1475 right: Box<Self>,
1476 },
1477
1478 DivFloat {
1479 location: SrcSpan,
1480 left: Box<Self>,
1481 right: Box<Self>,
1482 },
1483
1484 RemainderInt {
1485 location: SrcSpan,
1486 left: Box<Self>,
1487 right: Box<Self>,
1488 },
1489
1490 Or {
1491 location: SrcSpan,
1492 left: Box<Self>,
1493 right: Box<Self>,
1494 },
1495
1496 And {
1497 location: SrcSpan,
1498 left: Box<Self>,
1499 right: Box<Self>,
1500 },
1501
1502 Not {
1503 location: SrcSpan,
1504 expression: Box<Self>,
1505 },
1506
1507 Var {
1508 location: SrcSpan,
1509 type_: Type,
1510 name: EcoString,
1511 },
1512
1513 TupleIndex {
1514 location: SrcSpan,
1515 index: u64,
1516 type_: Type,
1517 tuple: Box<Self>,
1518 },
1519
1520 FieldAccess {
1521 location: SrcSpan,
1522 index: Option<u64>,
1523 label: EcoString,
1524 type_: Type,
1525 container: Box<Self>,
1526 },
1527
1528 ModuleSelect {
1529 location: SrcSpan,
1530 type_: Type,
1531 label: EcoString,
1532 module_name: EcoString,
1533 module_alias: EcoString,
1534 literal: Constant<Type, RecordTag>,
1535 },
1536
1537 Constant(Constant<Type, RecordTag>),
1538}
1539
1540impl<A, B> ClauseGuard<A, B> {
1541 pub fn location(&self) -> SrcSpan {
1542 match self {
1543 ClauseGuard::Constant(constant) => constant.location(),
1544 ClauseGuard::Or { location, .. }
1545 | ClauseGuard::And { location, .. }
1546 | ClauseGuard::Not { location, .. }
1547 | ClauseGuard::Var { location, .. }
1548 | ClauseGuard::TupleIndex { location, .. }
1549 | ClauseGuard::Equals { location, .. }
1550 | ClauseGuard::NotEquals { location, .. }
1551 | ClauseGuard::GtInt { location, .. }
1552 | ClauseGuard::GtEqInt { location, .. }
1553 | ClauseGuard::LtInt { location, .. }
1554 | ClauseGuard::LtEqInt { location, .. }
1555 | ClauseGuard::GtFloat { location, .. }
1556 | ClauseGuard::GtEqFloat { location, .. }
1557 | ClauseGuard::LtFloat { location, .. }
1558 | ClauseGuard::AddInt { location, .. }
1559 | ClauseGuard::AddFloat { location, .. }
1560 | ClauseGuard::SubInt { location, .. }
1561 | ClauseGuard::SubFloat { location, .. }
1562 | ClauseGuard::MultInt { location, .. }
1563 | ClauseGuard::MultFloat { location, .. }
1564 | ClauseGuard::DivInt { location, .. }
1565 | ClauseGuard::DivFloat { location, .. }
1566 | ClauseGuard::RemainderInt { location, .. }
1567 | ClauseGuard::FieldAccess { location, .. }
1568 | ClauseGuard::LtEqFloat { location, .. }
1569 | ClauseGuard::ModuleSelect { location, .. } => *location,
1570 }
1571 }
1572
1573 pub fn precedence(&self) -> u8 {
1574 // Ensure that this matches the other precedence function for guards
1575 match self.bin_op_name() {
1576 Some(name) => name.precedence(),
1577 None => u8::MAX,
1578 }
1579 }
1580
1581 pub fn bin_op_name(&self) -> Option<BinOp> {
1582 match self {
1583 ClauseGuard::Or { .. } => Some(BinOp::Or),
1584 ClauseGuard::And { .. } => Some(BinOp::And),
1585 ClauseGuard::Equals { .. } => Some(BinOp::Eq),
1586 ClauseGuard::NotEquals { .. } => Some(BinOp::NotEq),
1587 ClauseGuard::GtInt { .. } => Some(BinOp::GtInt),
1588 ClauseGuard::GtEqInt { .. } => Some(BinOp::GtEqInt),
1589 ClauseGuard::LtInt { .. } => Some(BinOp::LtInt),
1590 ClauseGuard::LtEqInt { .. } => Some(BinOp::LtEqInt),
1591 ClauseGuard::GtFloat { .. } => Some(BinOp::GtFloat),
1592 ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat),
1593 ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat),
1594 ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat),
1595 ClauseGuard::AddInt { .. } => Some(BinOp::AddInt),
1596 ClauseGuard::AddFloat { .. } => Some(BinOp::AddFloat),
1597 ClauseGuard::SubInt { .. } => Some(BinOp::SubInt),
1598 ClauseGuard::SubFloat { .. } => Some(BinOp::SubFloat),
1599 ClauseGuard::MultInt { .. } => Some(BinOp::MultInt),
1600 ClauseGuard::MultFloat { .. } => Some(BinOp::MultFloat),
1601 ClauseGuard::DivInt { .. } => Some(BinOp::DivInt),
1602 ClauseGuard::DivFloat { .. } => Some(BinOp::DivFloat),
1603 ClauseGuard::RemainderInt { .. } => Some(BinOp::RemainderInt),
1604
1605 ClauseGuard::Constant(_)
1606 | ClauseGuard::Var { .. }
1607 | ClauseGuard::Not { .. }
1608 | ClauseGuard::TupleIndex { .. }
1609 | ClauseGuard::FieldAccess { .. }
1610 | ClauseGuard::ModuleSelect { .. } => None,
1611 }
1612 }
1613}
1614
1615impl TypedClauseGuard {
1616 pub fn type_(&self) -> Arc<Type> {
1617 match self {
1618 ClauseGuard::Var { type_, .. } => type_.clone(),
1619 ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
1620 ClauseGuard::FieldAccess { type_, .. } => type_.clone(),
1621 ClauseGuard::ModuleSelect { type_, .. } => type_.clone(),
1622 ClauseGuard::Constant(constant) => constant.type_(),
1623
1624 ClauseGuard::AddInt { .. }
1625 | ClauseGuard::SubInt { .. }
1626 | ClauseGuard::MultInt { .. }
1627 | ClauseGuard::DivInt { .. }
1628 | ClauseGuard::RemainderInt { .. } => type_::int(),
1629
1630 ClauseGuard::AddFloat { .. }
1631 | ClauseGuard::SubFloat { .. }
1632 | ClauseGuard::MultFloat { .. }
1633 | ClauseGuard::DivFloat { .. } => type_::float(),
1634
1635 ClauseGuard::Or { .. }
1636 | ClauseGuard::Not { .. }
1637 | ClauseGuard::And { .. }
1638 | ClauseGuard::Equals { .. }
1639 | ClauseGuard::NotEquals { .. }
1640 | ClauseGuard::GtInt { .. }
1641 | ClauseGuard::GtEqInt { .. }
1642 | ClauseGuard::LtInt { .. }
1643 | ClauseGuard::LtEqInt { .. }
1644 | ClauseGuard::GtFloat { .. }
1645 | ClauseGuard::GtEqFloat { .. }
1646 | ClauseGuard::LtFloat { .. }
1647 | ClauseGuard::LtEqFloat { .. } => type_::bool(),
1648 }
1649 }
1650}
1651
1652#[derive(Debug, PartialEq, Eq, Default, Clone, Copy, serde::Serialize, serde::Deserialize)]
1653pub struct SrcSpan {
1654 pub start: u32,
1655 pub end: u32,
1656}
1657
1658impl SrcSpan {
1659 pub fn new(start: u32, end: u32) -> Self {
1660 Self { start, end }
1661 }
1662
1663 pub fn contains(&self, byte_index: u32) -> bool {
1664 byte_index >= self.start && byte_index <= self.end
1665 }
1666}
1667
1668#[derive(Debug, PartialEq, Eq, Clone)]
1669pub struct DefinitionLocation<'module> {
1670 pub module: Option<&'module str>,
1671 pub span: SrcSpan,
1672}
1673
1674pub type UntypedPattern = Pattern<()>;
1675pub type TypedPattern = Pattern<Arc<Type>>;
1676
1677#[derive(Debug, Clone, PartialEq, Eq)]
1678pub enum Pattern<Type> {
1679 Int {
1680 location: SrcSpan,
1681 value: EcoString,
1682 int_value: BigInt,
1683 },
1684
1685 Float {
1686 location: SrcSpan,
1687 value: EcoString,
1688 },
1689
1690 String {
1691 location: SrcSpan,
1692 value: EcoString,
1693 },
1694
1695 /// The creation of a variable.
1696 /// e.g. `assert [this_is_a_var, .._] = x`
1697 Variable {
1698 location: SrcSpan,
1699 name: EcoString,
1700 type_: Type,
1701 },
1702
1703 /// A reference to a variable in a bit array. This is always a variable
1704 /// being used rather than a new variable being assigned.
1705 /// e.g. `assert <<y:size(somevar)>> = x`
1706 VarUsage {
1707 location: SrcSpan,
1708 name: EcoString,
1709 constructor: Option<ValueConstructor>,
1710 type_: Type,
1711 },
1712
1713 /// A name given to a sub-pattern using the `as` keyword.
1714 /// e.g. `assert #(1, [_, _] as the_list) = x`
1715 Assign {
1716 name: EcoString,
1717 location: SrcSpan,
1718 pattern: Box<Self>,
1719 },
1720
1721 /// A pattern that binds to any value but does not assign a variable.
1722 /// Always starts with an underscore.
1723 Discard {
1724 name: EcoString,
1725 location: SrcSpan,
1726 type_: Type,
1727 },
1728
1729 List {
1730 location: SrcSpan,
1731 elements: Vec<Self>,
1732 tail: Option<Box<Self>>,
1733 type_: Type,
1734 },
1735
1736 /// The constructor for a custom type. Starts with an uppercase letter.
1737 Constructor {
1738 location: SrcSpan,
1739 name: EcoString,
1740 arguments: Vec<CallArg<Self>>,
1741 module: Option<(EcoString, SrcSpan)>,
1742 constructor: Inferred<PatternConstructor>,
1743 spread: Option<SrcSpan>,
1744 type_: Type,
1745 },
1746
1747 Tuple {
1748 location: SrcSpan,
1749 elems: Vec<Self>,
1750 },
1751
1752 BitArray {
1753 location: SrcSpan,
1754 segments: Vec<BitArraySegment<Self, Type>>,
1755 },
1756
1757 // "prefix" <> variable
1758 StringPrefix {
1759 location: SrcSpan,
1760 left_location: SrcSpan,
1761 left_side_assignment: Option<(EcoString, SrcSpan)>,
1762 right_location: SrcSpan,
1763 left_side_string: EcoString,
1764 /// The variable on the right hand side of the `<>`.
1765 right_side_assignment: AssignName,
1766 },
1767
1768 /// A placeholder pattern used to allow module analysis to continue
1769 /// even when there are type errors. Should never end up in generated code.
1770 Invalid {
1771 location: SrcSpan,
1772 type_: Type,
1773 },
1774}
1775
1776impl Default for Inferred<()> {
1777 fn default() -> Self {
1778 Self::Unknown
1779 }
1780}
1781
1782#[derive(Debug, Clone, PartialEq, Eq)]
1783pub enum AssignName {
1784 Variable(EcoString),
1785 Discard(EcoString),
1786}
1787
1788impl AssignName {
1789 pub fn name(&self) -> &EcoString {
1790 match self {
1791 AssignName::Variable(name) | AssignName::Discard(name) => name,
1792 }
1793 }
1794
1795 pub fn to_arg_names(self, location: SrcSpan) -> ArgNames {
1796 match self {
1797 AssignName::Variable(name) => ArgNames::Named { name, location },
1798 AssignName::Discard(name) => ArgNames::Discard { name, location },
1799 }
1800 }
1801
1802 pub fn assigned_name(&self) -> Option<&str> {
1803 match self {
1804 AssignName::Variable(name) => Some(name),
1805 AssignName::Discard(_) => None,
1806 }
1807 }
1808}
1809
1810impl<A> Pattern<A> {
1811 pub fn location(&self) -> SrcSpan {
1812 match self {
1813 Pattern::Assign {
1814 pattern, location, ..
1815 } => SrcSpan::new(pattern.location().start, location.end),
1816 Pattern::Int { location, .. }
1817 | Pattern::Variable { location, .. }
1818 | Pattern::VarUsage { location, .. }
1819 | Pattern::List { location, .. }
1820 | Pattern::Float { location, .. }
1821 | Pattern::Discard { location, .. }
1822 | Pattern::String { location, .. }
1823 | Pattern::Tuple { location, .. }
1824 | Pattern::Constructor { location, .. }
1825 | Pattern::StringPrefix { location, .. }
1826 | Pattern::BitArray { location, .. }
1827 | Pattern::Invalid { location, .. } => *location,
1828 }
1829 }
1830
1831 /// Returns `true` if the pattern is [`Discard`].
1832 ///
1833 /// [`Discard`]: Pattern::Discard
1834 pub fn is_discard(&self) -> bool {
1835 matches!(self, Self::Discard { .. })
1836 }
1837}
1838
1839impl TypedPattern {
1840 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
1841 match self {
1842 Pattern::Int { .. }
1843 | Pattern::Float { .. }
1844 | Pattern::String { .. }
1845 | Pattern::Variable { .. }
1846 | Pattern::VarUsage { .. }
1847 | Pattern::Assign { .. }
1848 | Pattern::Discard { .. }
1849 | Pattern::List { .. }
1850 | Pattern::Tuple { .. }
1851 | Pattern::BitArray { .. }
1852 | Pattern::StringPrefix { .. }
1853 | Pattern::Invalid { .. } => None,
1854
1855 Pattern::Constructor { constructor, .. } => constructor.definition_location(),
1856 }
1857 }
1858
1859 pub fn get_documentation(&self) -> Option<&str> {
1860 match self {
1861 Pattern::Int { .. }
1862 | Pattern::Float { .. }
1863 | Pattern::String { .. }
1864 | Pattern::Variable { .. }
1865 | Pattern::VarUsage { .. }
1866 | Pattern::Assign { .. }
1867 | Pattern::Discard { .. }
1868 | Pattern::List { .. }
1869 | Pattern::Tuple { .. }
1870 | Pattern::BitArray { .. }
1871 | Pattern::StringPrefix { .. }
1872 | Pattern::Invalid { .. } => None,
1873
1874 Pattern::Constructor { constructor, .. } => constructor.get_documentation(),
1875 }
1876 }
1877
1878 pub fn type_(&self) -> Arc<Type> {
1879 match self {
1880 Pattern::Int { .. } => type_::int(),
1881 Pattern::Float { .. } => type_::float(),
1882 Pattern::String { .. } => type_::string(),
1883 Pattern::BitArray { .. } => type_::bits(),
1884 Pattern::StringPrefix { .. } => type_::string(),
1885
1886 Pattern::Variable { type_, .. }
1887 | Pattern::List { type_, .. }
1888 | Pattern::VarUsage { type_, .. }
1889 | Pattern::Constructor { type_, .. }
1890 | Pattern::Invalid { type_, .. } => type_.clone(),
1891
1892 Pattern::Assign { pattern, .. } => pattern.type_(),
1893
1894 Pattern::Discard { type_, .. } => type_.clone(),
1895
1896 Pattern::Tuple { elems, .. } => type_::tuple(elems.iter().map(|p| p.type_()).collect()),
1897 }
1898 }
1899
1900 fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1901 if !self.location().contains(byte_index) {
1902 return None;
1903 }
1904
1905 if let Pattern::Variable { name, .. } = self {
1906 // For pipes the pattern can't be pointed to
1907 if name.as_str().eq(PIPE_VARIABLE) {
1908 return None;
1909 }
1910 }
1911
1912 match self {
1913 Pattern::Int { .. }
1914 | Pattern::Float { .. }
1915 | Pattern::String { .. }
1916 | Pattern::Variable { .. }
1917 | Pattern::VarUsage { .. }
1918 | Pattern::Assign { .. }
1919 | Pattern::Discard { .. }
1920 | Pattern::BitArray { .. }
1921 | Pattern::StringPrefix { .. }
1922 | Pattern::Invalid { .. } => Some(Located::Pattern(self)),
1923
1924 Pattern::Constructor {
1925 arguments, spread, ..
1926 } => match spread {
1927 Some(spread_location) if spread_location.contains(byte_index) => {
1928 Some(Located::PatternSpread {
1929 spread_location: *spread_location,
1930 arguments,
1931 })
1932 }
1933
1934 Some(_) | None => arguments.iter().find_map(|arg| arg.find_node(byte_index)),
1935 },
1936 Pattern::List { elements, tail, .. } => elements
1937 .iter()
1938 .find_map(|p| p.find_node(byte_index))
1939 .or_else(|| tail.as_ref().and_then(|p| p.find_node(byte_index))),
1940
1941 Pattern::Tuple { elems, .. } => elems.iter().find_map(|p| p.find_node(byte_index)),
1942 }
1943 .or(Some(Located::Pattern(self)))
1944 }
1945}
1946impl<A> HasLocation for Pattern<A> {
1947 fn location(&self) -> SrcSpan {
1948 self.location()
1949 }
1950}
1951
1952#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1953pub enum AssignmentKind {
1954 // let x = ...
1955 Let,
1956 // let assert x = ...
1957 Assert { location: SrcSpan },
1958}
1959
1960impl AssignmentKind {
1961 /// Returns `true` if the assignment kind is [`Assert`].
1962 ///
1963 /// [`Assert`]: AssignmentKind::Assert
1964 #[must_use]
1965 pub fn is_assert(&self) -> bool {
1966 match self {
1967 Self::Assert { .. } => true,
1968 Self::Let => false,
1969 }
1970 }
1971}
1972
1973// BitArrays
1974
1975pub type UntypedExprBitArraySegment = BitArraySegment<UntypedExpr, ()>;
1976pub type TypedExprBitArraySegment = BitArraySegment<TypedExpr, Arc<Type>>;
1977
1978pub type UntypedConstantBitArraySegment = BitArraySegment<UntypedConstant, ()>;
1979pub type TypedConstantBitArraySegment = BitArraySegment<TypedConstant, Arc<Type>>;
1980
1981pub type UntypedPatternBitArraySegment = BitArraySegment<UntypedPattern, ()>;
1982pub type TypedPatternBitArraySegment = BitArraySegment<TypedPattern, Arc<Type>>;
1983
1984#[derive(Debug, Clone, PartialEq, Eq)]
1985pub struct BitArraySegment<Value, Type> {
1986 pub location: SrcSpan,
1987 pub value: Box<Value>,
1988 pub options: Vec<BitArrayOption<Value>>,
1989 pub type_: Type,
1990}
1991
1992impl TypedExprBitArraySegment {
1993 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1994 self.value.find_node(byte_index)
1995 }
1996}
1997
1998pub type TypedConstantBitArraySegmentOption = BitArrayOption<TypedConstant>;
1999
2000#[derive(Debug, PartialEq, Eq, Clone)]
2001pub enum BitArrayOption<Value> {
2002 Bytes {
2003 location: SrcSpan,
2004 },
2005
2006 Int {
2007 location: SrcSpan,
2008 },
2009
2010 Float {
2011 location: SrcSpan,
2012 },
2013
2014 Bits {
2015 location: SrcSpan,
2016 },
2017
2018 Utf8 {
2019 location: SrcSpan,
2020 },
2021
2022 Utf16 {
2023 location: SrcSpan,
2024 },
2025
2026 Utf32 {
2027 location: SrcSpan,
2028 },
2029
2030 Utf8Codepoint {
2031 location: SrcSpan,
2032 },
2033
2034 Utf16Codepoint {
2035 location: SrcSpan,
2036 },
2037
2038 Utf32Codepoint {
2039 location: SrcSpan,
2040 },
2041
2042 Signed {
2043 location: SrcSpan,
2044 },
2045
2046 Unsigned {
2047 location: SrcSpan,
2048 },
2049
2050 Big {
2051 location: SrcSpan,
2052 },
2053
2054 Little {
2055 location: SrcSpan,
2056 },
2057
2058 Native {
2059 location: SrcSpan,
2060 },
2061
2062 Size {
2063 location: SrcSpan,
2064 value: Box<Value>,
2065 short_form: bool,
2066 },
2067
2068 Unit {
2069 location: SrcSpan,
2070 value: u8,
2071 },
2072}
2073
2074impl<A> BitArrayOption<A> {
2075 pub fn value(&self) -> Option<&A> {
2076 match self {
2077 BitArrayOption::Size { value, .. } => Some(value),
2078 _ => None,
2079 }
2080 }
2081
2082 pub fn location(&self) -> SrcSpan {
2083 match self {
2084 BitArrayOption::Bytes { location }
2085 | BitArrayOption::Int { location }
2086 | BitArrayOption::Float { location }
2087 | BitArrayOption::Bits { location }
2088 | BitArrayOption::Utf8 { location }
2089 | BitArrayOption::Utf16 { location }
2090 | BitArrayOption::Utf32 { location }
2091 | BitArrayOption::Utf8Codepoint { location }
2092 | BitArrayOption::Utf16Codepoint { location }
2093 | BitArrayOption::Utf32Codepoint { location }
2094 | BitArrayOption::Signed { location }
2095 | BitArrayOption::Unsigned { location }
2096 | BitArrayOption::Big { location }
2097 | BitArrayOption::Little { location }
2098 | BitArrayOption::Native { location }
2099 | BitArrayOption::Size { location, .. }
2100 | BitArrayOption::Unit { location, .. } => *location,
2101 }
2102 }
2103
2104 pub fn label(&self) -> EcoString {
2105 match self {
2106 BitArrayOption::Bytes { .. } => "bytes".into(),
2107 BitArrayOption::Int { .. } => "int".into(),
2108 BitArrayOption::Float { .. } => "float".into(),
2109 BitArrayOption::Bits { .. } => "bits".into(),
2110 BitArrayOption::Utf8 { .. } => "utf8".into(),
2111 BitArrayOption::Utf16 { .. } => "utf16".into(),
2112 BitArrayOption::Utf32 { .. } => "utf32".into(),
2113 BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".into(),
2114 BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".into(),
2115 BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".into(),
2116 BitArrayOption::Signed { .. } => "signed".into(),
2117 BitArrayOption::Unsigned { .. } => "unsigned".into(),
2118 BitArrayOption::Big { .. } => "big".into(),
2119 BitArrayOption::Little { .. } => "little".into(),
2120 BitArrayOption::Native { .. } => "native".into(),
2121 BitArrayOption::Size { .. } => "size".into(),
2122 BitArrayOption::Unit { .. } => "unit".into(),
2123 }
2124 }
2125}
2126
2127#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2128pub enum TodoKind {
2129 Keyword,
2130 EmptyFunction,
2131 IncompleteUse,
2132}
2133
2134#[derive(Debug, Default)]
2135pub struct GroupedStatements {
2136 pub functions: Vec<UntypedFunction>,
2137 pub constants: Vec<UntypedModuleConstant>,
2138 pub custom_types: Vec<UntypedCustomType>,
2139 pub imports: Vec<UntypedImport>,
2140 pub type_aliases: Vec<UntypedTypeAlias>,
2141}
2142
2143impl GroupedStatements {
2144 pub fn new(statements: impl IntoIterator<Item = UntypedDefinition>) -> Self {
2145 let mut this = Self::default();
2146
2147 for statement in statements {
2148 this.add(statement)
2149 }
2150
2151 this
2152 }
2153
2154 pub fn is_empty(&self) -> bool {
2155 self.len() == 0
2156 }
2157
2158 pub fn len(&self) -> usize {
2159 let Self {
2160 custom_types,
2161 functions,
2162 constants,
2163 imports,
2164 type_aliases,
2165 } = self;
2166 functions.len() + constants.len() + imports.len() + custom_types.len() + type_aliases.len()
2167 }
2168
2169 fn add(&mut self, statement: UntypedDefinition) {
2170 match statement {
2171 Definition::Import(i) => self.imports.push(i),
2172 Definition::Function(f) => self.functions.push(f),
2173 Definition::TypeAlias(t) => self.type_aliases.push(t),
2174 Definition::CustomType(c) => self.custom_types.push(c),
2175 Definition::ModuleConstant(c) => self.constants.push(c),
2176 }
2177 }
2178}
2179
2180/// A statement with in a function body.
2181#[derive(Debug, Clone, PartialEq, Eq)]
2182pub enum Statement<TypeT, ExpressionT> {
2183 /// A bare expression that is not assigned to any variable.
2184 Expression(ExpressionT),
2185 /// Assigning an expression to variables using a pattern.
2186 Assignment(Assignment<TypeT, ExpressionT>),
2187 /// A `use` expression.
2188 Use(Use),
2189}
2190
2191pub type TypedStatement = Statement<Arc<Type>, TypedExpr>;
2192pub type UntypedStatement = Statement<(), UntypedExpr>;
2193
2194impl<T, E> Statement<T, E> {
2195 /// Returns `true` if the statement is [`Expression`].
2196 ///
2197 /// [`Expression`]: Statement::Expression
2198 #[must_use]
2199 pub fn is_expression(&self) -> bool {
2200 matches!(self, Self::Expression(..))
2201 }
2202
2203 #[must_use]
2204 pub(crate) fn is_use(&self) -> bool {
2205 match self {
2206 Self::Use(_) => true,
2207 _ => false,
2208 }
2209 }
2210}
2211
2212impl UntypedStatement {
2213 pub fn location(&self) -> SrcSpan {
2214 match self {
2215 Statement::Expression(expression) => expression.location(),
2216 Statement::Assignment(assignment) => assignment.location,
2217 Statement::Use(use_) => use_.location,
2218 }
2219 }
2220
2221 pub fn start_byte_index(&self) -> u32 {
2222 match self {
2223 Statement::Expression(expression) => expression.start_byte_index(),
2224 Statement::Assignment(assignment) => assignment.location.start,
2225 Statement::Use(use_) => use_.location.start,
2226 }
2227 }
2228
2229 pub fn is_placeholder(&self) -> bool {
2230 match self {
2231 Statement::Expression(expression) => expression.is_placeholder(),
2232 Statement::Assignment(_) | Statement::Use(_) => false,
2233 }
2234 }
2235}
2236
2237impl TypedStatement {
2238 pub fn is_println(&self) -> bool {
2239 match self {
2240 Statement::Expression(e) => e.is_println(),
2241 Statement::Assignment(_) => false,
2242 Statement::Use(_) => false,
2243 }
2244 }
2245
2246 pub fn is_non_pipe_expression(&self) -> bool {
2247 match self {
2248 Statement::Expression(expression) => !expression.is_pipeline(),
2249 _ => false,
2250 }
2251 }
2252
2253 pub fn location(&self) -> SrcSpan {
2254 match self {
2255 Statement::Expression(expression) => expression.location(),
2256 Statement::Assignment(assignment) => assignment.location,
2257 Statement::Use(use_) => use_.location,
2258 }
2259 }
2260
2261 pub fn type_(&self) -> Arc<Type> {
2262 match self {
2263 Statement::Expression(expression) => expression.type_(),
2264 Statement::Assignment(assignment) => assignment.type_(),
2265 Statement::Use(_use) => unreachable!("Use must not exist for typed code"),
2266 }
2267 }
2268
2269 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
2270 match self {
2271 Statement::Expression(expression) => expression.definition_location(),
2272 Statement::Assignment(_) => None,
2273 Statement::Use(_) => None,
2274 }
2275 }
2276
2277 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2278 match self {
2279 Statement::Use(_) => None,
2280 Statement::Expression(expression) => expression.find_node(byte_index),
2281 Statement::Assignment(assignment) => assignment.find_node(byte_index).or_else(|| {
2282 if assignment.location.contains(byte_index) {
2283 Some(Located::Statement(self))
2284 } else {
2285 None
2286 }
2287 }),
2288 }
2289 }
2290
2291 pub fn type_defining_location(&self) -> SrcSpan {
2292 match self {
2293 Statement::Expression(expression) => expression.type_defining_location(),
2294 Statement::Assignment(assignment) => assignment.location,
2295 Statement::Use(use_) => use_.location,
2296 }
2297 }
2298}
2299
2300#[derive(Debug, Clone, PartialEq, Eq)]
2301pub struct Assignment<TypeT, ExpressionT> {
2302 pub location: SrcSpan,
2303 pub value: Box<ExpressionT>,
2304 pub pattern: Pattern<TypeT>,
2305 pub kind: AssignmentKind,
2306 pub annotation: Option<TypeAst>,
2307}
2308
2309pub type TypedAssignment = Assignment<Arc<Type>, TypedExpr>;
2310pub type UntypedAssignment = Assignment<(), UntypedExpr>;
2311
2312impl TypedAssignment {
2313 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2314 if let Some(annotation) = &self.annotation {
2315 if let Some(l) = annotation.find_node(byte_index, self.pattern.type_()) {
2316 return Some(l);
2317 }
2318 }
2319 self.pattern
2320 .find_node(byte_index)
2321 .or_else(|| self.value.find_node(byte_index))
2322 }
2323
2324 pub fn type_(&self) -> Arc<Type> {
2325 self.value.type_()
2326 }
2327}
2328
2329#[derive(Debug, Clone, PartialEq, Eq)]
2330pub struct UseAssignment {
2331 pub location: SrcSpan,
2332 pub pattern: UntypedPattern,
2333 pub annotation: Option<TypeAst>,
2334}