Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

make qualified type annotations fault tolerant

Before this, when typing an invalid type the language server would mess up and
start suggesting completions for values rather than for types!

```gleam
pub fn wibble() -> option.|
// would show some invalid completions for all the option _values_
// rather than just the `option.Option` type.
```

We had to deal with something similar to make module accesses at the value level
fault tolerant: the parser doesn't fail when there's a select that has nothing
after the `.` (like `module_name.`).
So I ended up implementing the same thing but for type annotations as well:
the parser now allows `module_name.` with no type name following it and the
error is moved to the analysis step.

Making it fault tolerant fixed completions!

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Mar 26, 2026, 7:12 PM UTC) commit 4a16531b parent 744e0698 change-id nnzkskuy
+644 -257
+4
CHANGELOG.md
··· 137 137 record when writing a record update. 138 138 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 139 139 140 + - The language server no longer shows completions for values when editing a 141 + qualified type. 142 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 143 + 140 144 ### Formatter 141 145 142 146 - The formatter no longer moves comments out of type annotations.
+11 -7
compiler-core/src/analyse.rs
··· 2150 2150 TypeAst::Var(TypeAstVar { .. }) => (), 2151 2151 TypeAst::Hole(TypeAstHole { .. }) => (), 2152 2152 TypeAst::Constructor(TypeAstConstructor { 2153 - name, 2154 - arguments, 2155 - module, 2156 - .. 2153 + name, arguments, .. 2157 2154 }) => { 2158 - deps.push(match module { 2159 - Some((module, _)) => format!("{name}.{module}").into(), 2160 - None => name.clone(), 2155 + deps.push(match name { 2156 + ast::TypeAstConstructorName::Unqualified { name, .. } => name.clone(), 2157 + ast::TypeAstConstructorName::Qualified { 2158 + module, 2159 + name: Some((name, _)), 2160 + .. 2161 + } => format!("{module}.{name}").into(), 2162 + ast::TypeAstConstructorName::Qualified { 2163 + module, name: None, .. 2164 + } => format!("{module}.").into(), 2161 2165 }); 2162 2166 2163 2167 for arg in arguments {
+164 -51
compiler-core/src/ast.rs
··· 328 328 #[derive(Debug, Clone, PartialEq, Eq)] 329 329 pub struct TypeAstConstructor { 330 330 pub location: SrcSpan, 331 - pub name_location: SrcSpan, 332 - pub module: Option<(EcoString, SrcSpan)>, 333 - pub name: EcoString, 331 + pub name: TypeAstConstructorName, 334 332 pub arguments: Vec<TypeAst>, 335 333 pub start_parentheses: Option<u32>, 336 334 } 337 335 336 + /// This represents a type constructor name, that can either be qualified, or 337 + /// unqualified. 338 + #[derive(Debug, Clone, PartialEq, Eq)] 339 + pub enum TypeAstConstructorName { 340 + /// ```gleam 341 + /// pub fn wibble() -> wibble.Wibble 342 + /// // ^^^^^^ module 343 + /// // ^^^^^^ name 344 + /// ``` 345 + /// Notice how the name could be missing, this is an error! However, instead 346 + /// of treating it like a syntax error we allow parsing it and report it 347 + /// later. This way the language server can provide better help! 348 + Qualified { 349 + module: EcoString, 350 + module_location: SrcSpan, 351 + dot_location: u32, 352 + name: Option<(EcoString, SrcSpan)>, 353 + }, 354 + 355 + /// ```gleam 356 + /// pub fn wibble() -> Wibble 357 + /// // ^^^^^^ name 358 + /// ``` 359 + Unqualified { name: EcoString, location: SrcSpan }, 360 + } 361 + 362 + impl TypeAstConstructorName { 363 + pub fn is_qualified(&self) -> bool { 364 + match self { 365 + TypeAstConstructorName::Qualified { .. } => true, 366 + TypeAstConstructorName::Unqualified { .. } => false, 367 + } 368 + } 369 + 370 + pub fn module_name(&self) -> Option<&EcoString> { 371 + match self { 372 + TypeAstConstructorName::Qualified { module, .. } => Some(module), 373 + TypeAstConstructorName::Unqualified { .. } => None, 374 + } 375 + } 376 + 377 + pub fn name(&self) -> Option<&EcoString> { 378 + match self { 379 + TypeAstConstructorName::Unqualified { name, .. } 380 + | TypeAstConstructorName::Qualified { 381 + name: Some((name, _)), 382 + .. 383 + } => Some(name), 384 + 385 + TypeAstConstructorName::Qualified { name: None, .. } => None, 386 + } 387 + } 388 + 389 + pub fn name_location(&self) -> Option<SrcSpan> { 390 + match self { 391 + TypeAstConstructorName::Unqualified { location, .. } 392 + | TypeAstConstructorName::Qualified { 393 + name: Some((_, location)), 394 + .. 395 + } => Some(*location), 396 + 397 + TypeAstConstructorName::Qualified { name: None, .. } => None, 398 + } 399 + } 400 + 401 + fn is_logically_equal(&self, other: &TypeAstConstructorName) -> bool { 402 + match (self, other) { 403 + ( 404 + TypeAstConstructorName::Qualified { module, name, .. }, 405 + TypeAstConstructorName::Qualified { 406 + module: other_module, 407 + name: other_name, 408 + .. 409 + }, 410 + ) => { 411 + module == other_module 412 + && match (name, other_name) { 413 + (Some((name, _)), Some((other_name, _))) => name == other_name, 414 + (None, Some(_)) | (Some(_), None) => false, 415 + (None, None) => true, 416 + } 417 + } 418 + 419 + ( 420 + TypeAstConstructorName::Unqualified { name, location: _ }, 421 + TypeAstConstructorName::Unqualified { 422 + name: other_name, 423 + location: _, 424 + }, 425 + ) => name == other_name, 426 + 427 + ( 428 + TypeAstConstructorName::Qualified { .. }, 429 + TypeAstConstructorName::Unqualified { .. }, 430 + ) 431 + | ( 432 + TypeAstConstructorName::Unqualified { .. }, 433 + TypeAstConstructorName::Qualified { .. }, 434 + ) => false, 435 + } 436 + } 437 + } 438 + 338 439 #[derive(Debug, Clone, PartialEq, Eq)] 339 440 pub struct TypeAstFn { 340 441 pub location: SrcSpan, ··· 383 484 pub fn is_logically_equal(&self, other: &TypeAst) -> bool { 384 485 match self { 385 486 TypeAst::Constructor(TypeAstConstructor { 386 - module, 387 487 name, 388 488 arguments, 389 489 location: _, 390 - name_location: _, 391 490 start_parentheses: _, 392 491 }) => match other { 393 492 TypeAst::Constructor(TypeAstConstructor { 394 - module: o_module, 395 - name: o_name, 396 - arguments: o_arguments, 493 + name: other_name, 494 + arguments: other_arguments, 397 495 location: _, 398 - name_location: _, 399 496 start_parentheses: _, 400 497 }) => { 401 - let module_name = 402 - |m: &Option<(EcoString, _)>| m.as_ref().map(|(m, _)| m.clone()); 403 - module_name(module) == module_name(o_module) 404 - && name == o_name 405 - && arguments.len() == o_arguments.len() 498 + name.is_logically_equal(other_name) 499 + && arguments.len() == other_arguments.len() 406 500 && arguments 407 501 .iter() 408 - .zip(o_arguments) 409 - .all(|a| a.0.is_logically_equal(a.1)) 502 + .zip(other_arguments) 503 + .all(|argument| argument.0.is_logically_equal(argument.1)) 410 504 } 411 505 TypeAst::Fn(_) | TypeAst::Var(_) | TypeAst::Tuple(_) | TypeAst::Hole(_) => false, 412 506 }, ··· 497 591 }) 498 592 .or(Some(Located::Annotation { ast: self, type_ })), 499 593 TypeAst::Constructor(TypeAstConstructor { 500 - arguments, module, .. 501 - }) => type_ 502 - .named_type_information() 503 - .and_then(|(module_name, _, arg_types)| { 504 - if let Some(arg) = arguments 505 - .iter() 506 - .zip(arg_types) 507 - .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone())) 508 - { 509 - return Some(arg); 510 - } 594 + arguments, name, .. 595 + }) => { 596 + // 597 + type_ 598 + .named_type_information() 599 + .and_then(|(module_name, _, arg_types)| { 600 + if let Some(arg) = arguments 601 + .iter() 602 + .zip(arg_types) 603 + .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone())) 604 + { 605 + return Some(arg); 606 + } 511 607 512 - if let Some((module_alias, location)) = module 513 - && location.contains(byte_index) 514 - { 515 - return Some(Located::ModuleName { 516 - location: *location, 517 - module_name, 518 - module_alias: module_alias.clone(), 519 - layer: Layer::Type, 520 - }); 521 - } 608 + if let TypeAstConstructorName::Qualified { 609 + module_location, 610 + module: module_alias, 611 + .. 612 + } = name 613 + && module_location.contains(byte_index) 614 + { 615 + return Some(Located::ModuleName { 616 + location: *module_location, 617 + module_name, 618 + module_alias: module_alias.clone(), 619 + layer: Layer::Type, 620 + }); 621 + } 522 622 523 - None 524 - }) 525 - .or(Some(Located::Annotation { ast: self, type_ })), 623 + None 624 + }) 625 + .or(Some(Located::Annotation { ast: self, type_ })) 626 + } 526 627 TypeAst::Tuple(TypeAstTuple { elements, .. }) => type_ 527 628 .tuple_types() 528 629 .and_then(|elem_types| { ··· 569 670 func.return_.print(buffer); 570 671 } 571 672 TypeAst::Constructor(constructor) => { 572 - if let Some((module, _)) = &constructor.module { 573 - buffer.push_str(module); 574 - buffer.push('.'); 575 - } 576 - buffer.push_str(&constructor.name); 673 + match &constructor.name { 674 + TypeAstConstructorName::Unqualified { name, .. } => buffer.push_str(name), 675 + TypeAstConstructorName::Qualified { module, name, .. } => { 676 + buffer.push_str(module); 677 + buffer.push('.'); 678 + if let Some((name, _name_location)) = name { 679 + buffer.push_str(name); 680 + } 681 + } 682 + }; 683 + 577 684 if !constructor.arguments.is_empty() { 578 685 buffer.push('('); 579 686 for (i, argument) in constructor.arguments.iter().enumerate() { ··· 617 724 fn type_ast_print_constructor() { 618 725 let mut buffer = EcoString::new(); 619 726 let ast = TypeAst::Constructor(TypeAstConstructor { 620 - name: "SomeType".into(), 621 - module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })), 727 + name: TypeAstConstructorName::Qualified { 728 + module: "some_module".into(), 729 + dot_location: 1, 730 + module_location: SrcSpan { start: 1, end: 1 }, 731 + name: Some(("SomeType".into(), SrcSpan { start: 1, end: 1 })), 732 + }, 622 733 location: SrcSpan { start: 1, end: 1 }, 623 - name_location: SrcSpan { start: 1, end: 1 }, 624 734 arguments: vec![ 625 735 TypeAst::Var(TypeAstVar { 626 736 location: SrcSpan { start: 1, end: 1 }, ··· 644 754 location: SrcSpan { start: 1, end: 1 }, 645 755 elements: vec![ 646 756 TypeAst::Constructor(TypeAstConstructor { 647 - name: "SomeType".into(), 648 - module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })), 757 + name: TypeAstConstructorName::Qualified { 758 + module: "some_module".into(), 759 + module_location: SrcSpan { start: 1, end: 1 }, 760 + dot_location: 1, 761 + name: Some(("SomeType".into(), SrcSpan { start: 1, end: 1 })), 762 + }, 649 763 location: SrcSpan { start: 1, end: 1 }, 650 - name_location: SrcSpan { start: 1, end: 1 }, 651 764 arguments: vec![ 652 765 TypeAst::Var(TypeAstVar { 653 766 location: SrcSpan { start: 1, end: 1 },
+6 -21
compiler-core/src/ast/visit.rs
··· 41 41 use crate::{ 42 42 analyse::Inferred, 43 43 ast::{ 44 - BitArraySize, RecordBeingUpdated, TypedBitArraySize, TypedConstantBitArraySegment, 45 - TypedDefinitions, TypedImport, TypedTailPattern, TypedTypeAlias, typed::InvalidExpression, 44 + BitArraySize, RecordBeingUpdated, TypeAstConstructorName, TypedBitArraySize, 45 + TypedConstantBitArraySegment, TypedDefinitions, TypedImport, TypedTailPattern, 46 + TypedTypeAlias, typed::InvalidExpression, 46 47 }, 47 48 exhaustiveness::CompiledCase, 48 49 parse::LiteralFloatValue, ··· 608 609 fn visit_type_ast_constructor( 609 610 &mut self, 610 611 location: &'ast SrcSpan, 611 - name_location: &'ast SrcSpan, 612 - module: &'ast Option<(EcoString, SrcSpan)>, 613 - name: &'ast EcoString, 612 + name: &'ast TypeAstConstructorName, 614 613 arguments: &'ast [TypeAst], 615 614 inferred_arguments_types: Option<Vec<Arc<Type>>>, 616 615 ) { 617 - visit_type_ast_constructor( 618 - self, 619 - location, 620 - name_location, 621 - module, 622 - name, 623 - arguments, 624 - inferred_arguments_types, 625 - ); 616 + visit_type_ast_constructor(self, location, name, arguments, inferred_arguments_types); 626 617 } 627 618 628 619 fn visit_type_ast_fn( ··· 984 975 match node { 985 976 TypeAst::Constructor(super::TypeAstConstructor { 986 977 location, 987 - name_location, 988 978 arguments, 989 - module, 990 979 name, 991 980 start_parentheses: _, 992 981 }) => { 993 982 v.visit_type_ast_constructor( 994 983 location, 995 - name_location, 996 - module, 997 984 name, 998 985 arguments, 999 986 type_.and_then(|type_| type_.constructor_types()), ··· 1031 1018 pub fn visit_type_ast_constructor<'a, V>( 1032 1019 v: &mut V, 1033 1020 _location: &'a SrcSpan, 1034 - _name_location: &'a SrcSpan, 1035 - _module: &'a Option<(EcoString, SrcSpan)>, 1036 - _name: &'a EcoString, 1021 + _name: &'a TypeAstConstructorName, 1037 1022 arguments: &'a [TypeAst], 1038 1023 inferred_arguments_types: Option<Vec<Arc<Type>>>, 1039 1024 ) where
+16
compiler-core/src/error.rs
··· 2596 2596 } 2597 2597 }, 2598 2598 2599 + TypeError::QualifiedTypeMissingName { location } => Diagnostic { 2600 + title: "Invalid type".into(), 2601 + text: "".into(), 2602 + hint: None, 2603 + level: Level::Error, 2604 + location: Some(Location { 2605 + label: Label { 2606 + text: Some("This is not a valid type".into()), 2607 + span: *location, 2608 + }, 2609 + path: path.clone(), 2610 + src: src.clone(), 2611 + extra_labels: vec![], 2612 + }), 2613 + }, 2614 + 2599 2615 TypeError::UnknownType { 2600 2616 location, 2601 2617 name,
+11 -10
compiler-core/src/format.rs
··· 752 752 753 753 fn type_ast_constructor<'a>( 754 754 &mut self, 755 - module: &'a Option<(EcoString, SrcSpan)>, 756 - name: &'a str, 755 + name: &'a TypeAstConstructorName, 757 756 arguments: &'a [TypeAst], 758 757 location: &SrcSpan, 759 - _name_location: &SrcSpan, 760 758 ) -> Document<'a> { 761 - let head = module 762 - .as_ref() 763 - .map(|(qualifier, _)| qualifier.to_doc().append(".").append(name)) 764 - .unwrap_or_else(|| name.to_doc()); 759 + let head = match name { 760 + TypeAstConstructorName::Unqualified { name, .. } => name.to_doc(), 761 + TypeAstConstructorName::Qualified { module, name, .. } => { 762 + module.to_doc().append(".").append( 763 + name.as_ref() 764 + .map_or(nil(), |(name, _name_location)| name.to_doc()), 765 + ) 766 + } 767 + }; 765 768 766 769 if arguments.is_empty() { 767 770 head ··· 779 782 TypeAst::Constructor(TypeAstConstructor { 780 783 name, 781 784 arguments, 782 - module, 783 785 location, 784 - name_location, 785 786 start_parentheses: _, 786 - }) => self.type_ast_constructor(module, name, arguments, location, name_location), 787 + }) => self.type_ast_constructor(name, arguments, location), 787 788 788 789 TypeAst::Fn(TypeAstFn { 789 790 arguments,
+78 -38
compiler-core/src/parse.rs
··· 62 62 CustomType, Definition, Function, FunctionLiteralKind, HasLocation, Import, IntOperator, 63 63 Module, ModuleConstant, Pattern, Publicity, RecordBeingUpdated, RecordConstructor, 64 64 RecordConstructorArg, RecordUpdateArg, SrcSpan, Statement, TailPattern, TargetedDefinition, 65 - TodoKind, TypeAlias, TypeAst, TypeAstConstructor, TypeAstFn, TypeAstHole, TypeAstTuple, 66 - TypeAstVar, UnqualifiedImport, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedConstant, 67 - UntypedDefinition, UntypedExpr, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, 68 - UntypedStatement, UntypedUseAssignment, Use, UseAssignment, 65 + TodoKind, TypeAlias, TypeAst, TypeAstConstructor, TypeAstConstructorName, TypeAstFn, 66 + TypeAstHole, TypeAstTuple, TypeAstVar, UnqualifiedImport, UntypedArg, UntypedClause, 67 + UntypedClauseGuard, UntypedConstant, UntypedDefinition, UntypedExpr, UntypedModule, 68 + UntypedPattern, UntypedRecordUpdateArg, UntypedStatement, UntypedUseAssignment, Use, 69 + UseAssignment, 69 70 }; 70 71 use crate::build::Target; 71 72 use crate::error::wrap; ··· 2798 2799 // Constructor function 2799 2800 Some((start, Token::UpName { name }, end)) => { 2800 2801 self.advance(); 2801 - self.parse_type_name_finish(start, start, None, name, end) 2802 + let name = TypeAstConstructorName::Unqualified { 2803 + name, 2804 + location: SrcSpan::new(start, end), 2805 + }; 2806 + self.parse_type_name_finish(start, end, name) 2802 2807 } 2803 2808 2804 2809 // Constructor Module or type Variable 2805 - Some((start, Token::Name { name: mod_name }, end)) => { 2810 + Some((start, Token::Name { name: module }, end)) => { 2806 2811 self.advance(); 2807 - if self.maybe_one(&Token::Dot).is_some() { 2808 - let (name_start, upname, upname_e) = self.expect_upname()?; 2809 - self.parse_type_name_finish( 2810 - start, 2811 - name_start, 2812 - Some((mod_name, SrcSpan { start, end })), 2813 - upname, 2814 - upname_e, 2815 - ) 2812 + 2813 + if let Some((_, dot_end)) = self.maybe_one(&Token::Dot) { 2814 + let module_location = SrcSpan::new(start, end); 2815 + match self.maybe_upname() { 2816 + Some((name_start, name, name_end)) => { 2817 + let name = TypeAstConstructorName::Qualified { 2818 + module, 2819 + module_location, 2820 + dot_location: dot_end, 2821 + name: Some((name, SrcSpan::new(name_start, name_end))), 2822 + }; 2823 + self.parse_type_name_finish(start, name_end, name) 2824 + } 2825 + None => { 2826 + let name = TypeAstConstructorName::Qualified { 2827 + module, 2828 + module_location, 2829 + dot_location: dot_end, 2830 + name: None, 2831 + }; 2832 + self.parse_type_name_finish(start, dot_end, name) 2833 + } 2834 + } 2816 2835 } else { 2817 2836 Ok(Some(TypeAst::Var(TypeAstVar { 2818 2837 location: SrcSpan { start, end }, 2819 - name: mod_name, 2838 + name: module, 2820 2839 }))) 2821 2840 } 2822 2841 } ··· 2832 2851 fn parse_type_name_finish( 2833 2852 &mut self, 2834 2853 start: u32, 2835 - name_start: u32, 2836 - module: Option<(EcoString, SrcSpan)>, 2837 - name: EcoString, 2838 2854 end: u32, 2855 + name: TypeAstConstructorName, 2839 2856 ) -> Result<Option<TypeAst>, ParseError> { 2840 - if let Some((par_s, _)) = self.maybe_one(&Token::LeftParen) { 2857 + if let Some((left_paren_start, left_paren_end)) = self.maybe_one(&Token::LeftParen) { 2858 + // In case the type is qualified and is missing a name, it doesn't 2859 + // make sense to parse a types list: we don't want to accept 2860 + // something like `wibble.(a, b)`. 2861 + // Instead we want to say that `(` is unexpected and we were 2862 + // expecting a type name instead: 2863 + if name.name().is_none() { 2864 + return Err(ParseError { 2865 + error: ParseErrorType::ExpectedUpName, 2866 + location: SrcSpan::new(left_paren_start, left_paren_end), 2867 + }); 2868 + } 2869 + 2841 2870 let arguments = self.parse_types()?; 2842 - let (_, par_e) = self.expect_one(&Token::RightParen)?; 2871 + let (_, right_paren_end) = self.expect_one(&Token::RightParen)?; 2843 2872 Ok(Some(TypeAst::Constructor(TypeAstConstructor { 2844 - location: SrcSpan { start, end: par_e }, 2845 - name_location: SrcSpan { 2846 - start: name_start, 2847 - end, 2848 - }, 2849 - module, 2873 + location: SrcSpan::new(start, right_paren_end), 2850 2874 name, 2851 2875 arguments, 2852 - start_parentheses: Some(par_s), 2876 + start_parentheses: Some(left_paren_start), 2853 2877 }))) 2854 2878 } else if let Some((less_start, less_end)) = self.maybe_one(&Token::Less) { 2879 + let location = SrcSpan::new(less_start, less_end); 2880 + let (module, name) = match name { 2881 + TypeAstConstructorName::Qualified { 2882 + module, 2883 + name: Some((name, _)), 2884 + .. 2885 + } => (Some(module), name), 2886 + TypeAstConstructorName::Unqualified { name, .. } => (None, name), 2887 + 2888 + // If we're here it means someone has typed something truly 2889 + // wrong that looks like this: `wibble.<`. 2890 + // In this case the hint about angle brackets wouldn't make much 2891 + // sense, so we fallback to just reporting an invalid token 2892 + // error saying we were expecting an uppercase name 2893 + TypeAstConstructorName::Qualified { name: None, .. } => { 2894 + return Err(ParseError { 2895 + error: ParseErrorType::ExpectedUpName, 2896 + location, 2897 + }); 2898 + } 2899 + }; 2900 + 2901 + // Otherwise we try and report a nicer error, suggesting one should 2902 + // use `(a, b)` instead of `<a, b>`. 2855 2903 let arguments = self.parse_types()?; 2856 2904 Err(ParseError { 2905 + location, 2857 2906 error: ParseErrorType::TypeUsageAngleGenerics { 2858 2907 name, 2859 - module: module.map(|(module_name, _)| module_name), 2908 + module, 2860 2909 arguments, 2861 2910 }, 2862 - location: SrcSpan { 2863 - start: less_start, 2864 - end: less_end, 2865 - }, 2866 2911 }) 2867 2912 } else { 2868 2913 Ok(Some(TypeAst::Constructor(TypeAstConstructor { 2869 2914 location: SrcSpan { start, end }, 2870 - name_location: SrcSpan { 2871 - start: name_start, 2872 - end, 2873 - }, 2874 - module, 2875 2915 name, 2876 2916 arguments: vec![], 2877 2917 start_parentheses: None,
+18 -15
compiler-core/src/parse/snapshots/gleam_core__parse__tests__const_record_update_all_fields.snap
··· 50 50 start: 30, 51 51 end: 36, 52 52 }, 53 - name_location: SrcSpan { 54 - start: 30, 55 - end: 36, 53 + name: Unqualified { 54 + name: "String", 55 + location: SrcSpan { 56 + start: 30, 57 + end: 36, 58 + }, 56 59 }, 57 - module: None, 58 - name: "String", 59 60 arguments: [], 60 61 start_parentheses: None, 61 62 }, ··· 83 84 start: 43, 84 85 end: 46, 85 86 }, 86 - name_location: SrcSpan { 87 - start: 43, 88 - end: 46, 87 + name: Unqualified { 88 + name: "Int", 89 + location: SrcSpan { 90 + start: 43, 91 + end: 46, 92 + }, 89 93 }, 90 - module: None, 91 - name: "Int", 92 94 arguments: [], 93 95 start_parentheses: None, 94 96 }, ··· 116 118 start: 54, 117 119 end: 60, 118 120 }, 119 - name_location: SrcSpan { 120 - start: 54, 121 - end: 60, 121 + name: Unqualified { 122 + name: "String", 123 + location: SrcSpan { 124 + start: 54, 125 + end: 60, 126 + }, 122 127 }, 123 - module: None, 124 - name: "String", 125 128 arguments: [], 126 129 start_parentheses: None, 127 130 },
+12 -10
compiler-core/src/parse/snapshots/gleam_core__parse__tests__const_record_update_basic.snap
··· 50 50 start: 30, 51 51 end: 36, 52 52 }, 53 - name_location: SrcSpan { 54 - start: 30, 55 - end: 36, 53 + name: Unqualified { 54 + name: "String", 55 + location: SrcSpan { 56 + start: 30, 57 + end: 36, 58 + }, 56 59 }, 57 - module: None, 58 - name: "String", 59 60 arguments: [], 60 61 start_parentheses: None, 61 62 }, ··· 83 84 start: 43, 84 85 end: 46, 85 86 }, 86 - name_location: SrcSpan { 87 - start: 43, 88 - end: 46, 87 + name: Unqualified { 88 + name: "Int", 89 + location: SrcSpan { 90 + start: 43, 91 + end: 46, 92 + }, 89 93 }, 90 - module: None, 91 - name: "Int", 92 94 arguments: [], 93 95 start_parentheses: None, 94 96 },
+12 -10
compiler-core/src/parse/snapshots/gleam_core__parse__tests__const_record_update_only.snap
··· 50 50 start: 30, 51 51 end: 36, 52 52 }, 53 - name_location: SrcSpan { 54 - start: 30, 55 - end: 36, 53 + name: Unqualified { 54 + name: "String", 55 + location: SrcSpan { 56 + start: 30, 57 + end: 36, 58 + }, 56 59 }, 57 - module: None, 58 - name: "String", 59 60 arguments: [], 60 61 start_parentheses: None, 61 62 }, ··· 83 84 start: 43, 84 85 end: 46, 85 86 }, 86 - name_location: SrcSpan { 87 - start: 43, 88 - end: 46, 87 + name: Unqualified { 88 + name: "Int", 89 + location: SrcSpan { 90 + start: 43, 91 + end: 46, 92 + }, 89 93 }, 90 - module: None, 91 - name: "Int", 92 94 arguments: [], 93 95 start_parentheses: None, 94 96 },
+13
compiler-core/src/parse/snapshots/gleam_core__parse__tests__error_for_greater_sign_after_invalid_qualified_type.snap
··· 1 + --- 2 + source: compiler-core/src/parse/tests.rs 3 + expression: "pub fn main() -> wibble.<a> { todo }" 4 + --- 5 + ----- SOURCE CODE 6 + pub fn main() -> wibble.<a> { todo } 7 + 8 + ----- ERROR 9 + error: Syntax error 10 + ┌─ /src/parse/error.gleam:1:25 11 + 12 + 1 │ pub fn main() -> wibble.<a> { todo } 13 + │ ^ I was expecting a type name here
+13
compiler-core/src/parse/snapshots/gleam_core__parse__tests__parse_error_for_greater_sign_after_invalid_qualified_type.snap
··· 1 + --- 2 + source: compiler-core/src/parse/tests.rs 3 + expression: "pub fn main() -> wibble.<a> { todo }" 4 + --- 5 + ----- SOURCE CODE 6 + pub fn main() -> wibble.<a> { todo } 7 + 8 + ----- ERROR 9 + error: Syntax error 10 + ┌─ /src/parse/error.gleam:1:25 11 + 12 + 1 │ pub fn main() -> wibble.<a> { todo } 13 + │ ^ I was expecting a type name here
+13
compiler-core/src/parse/snapshots/gleam_core__parse__tests__parse_error_for_type_list_after_invalid_qualified_type_1.snap
··· 1 + --- 2 + source: compiler-core/src/parse/tests.rs 3 + expression: "pub fn main() -> wibble.() { todo }" 4 + --- 5 + ----- SOURCE CODE 6 + pub fn main() -> wibble.() { todo } 7 + 8 + ----- ERROR 9 + error: Syntax error 10 + ┌─ /src/parse/error.gleam:1:25 11 + 12 + 1 │ pub fn main() -> wibble.() { todo } 13 + │ ^ I was expecting a type name here
+13
compiler-core/src/parse/snapshots/gleam_core__parse__tests__parse_error_for_type_list_after_invalid_qualified_type_2.snap
··· 1 + --- 2 + source: compiler-core/src/parse/tests.rs 3 + expression: "pub fn main() -> wibble.(a, b) { todo }" 4 + --- 5 + ----- SOURCE CODE 6 + pub fn main() -> wibble.(a, b) { todo } 7 + 8 + ----- ERROR 9 + error: Syntax error 10 + ┌─ /src/parse/error.gleam:1:25 11 + 12 + 1 │ pub fn main() -> wibble.(a, b) { todo } 13 + │ ^ I was expecting a type name here
+6 -5
compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap
··· 50 50 start: 34, 51 51 end: 40, 52 52 }, 53 - name_location: SrcSpan { 54 - start: 34, 55 - end: 40, 53 + name: Unqualified { 54 + name: "String", 55 + location: SrcSpan { 56 + start: 34, 57 + end: 40, 58 + }, 56 59 }, 57 - module: None, 58 - name: "String", 59 60 arguments: [], 60 61 start_parentheses: None, 61 62 },
+12 -10
compiler-core/src/parse/snapshots/gleam_core__parse__tests__with_let_binding3_and_annotation.snap
··· 69 69 start: 16, 70 70 end: 25, 71 71 }, 72 - name_location: SrcSpan { 73 - start: 16, 74 - end: 20, 72 + name: Unqualified { 73 + name: "List", 74 + location: SrcSpan { 75 + start: 16, 76 + end: 20, 77 + }, 75 78 }, 76 - module: None, 77 - name: "List", 78 79 arguments: [ 79 80 Constructor( 80 81 TypeAstConstructor { ··· 82 83 start: 21, 83 84 end: 24, 84 85 }, 85 - name_location: SrcSpan { 86 - start: 21, 87 - end: 24, 86 + name: Unqualified { 87 + name: "Int", 88 + location: SrcSpan { 89 + start: 21, 90 + end: 24, 91 + }, 88 92 }, 89 - module: None, 90 - name: "Int", 91 93 arguments: [], 92 94 start_parentheses: None, 93 95 },
+15
compiler-core/src/parse/tests.rs
··· 2159 2159 " 2160 2160 ); 2161 2161 } 2162 + 2163 + #[test] 2164 + fn parse_error_for_greater_sign_after_invalid_qualified_type() { 2165 + assert_module_error!("pub fn main() -> wibble.<a> { todo }"); 2166 + } 2167 + 2168 + #[test] 2169 + fn parse_error_for_type_list_after_invalid_qualified_type_1() { 2170 + assert_module_error!("pub fn main() -> wibble.() { todo }"); 2171 + } 2172 + 2173 + #[test] 2174 + fn parse_error_for_type_list_after_invalid_qualified_type_2() { 2175 + assert_module_error!("pub fn main() -> wibble.(a, b) { todo }"); 2176 + }
+9 -2
compiler-core/src/type_/error.rs
··· 175 175 hint: UnknownTypeHint, 176 176 }, 177 177 178 + /// This happens when someone writes `module_name.` with no type name after 179 + /// it. 180 + QualifiedTypeMissingName { 181 + location: SrcSpan, 182 + }, 183 + 178 184 UnknownModule { 179 185 location: SrcSpan, 180 186 name: EcoString, ··· 1364 1370 | Error::SrcImportingDevDependency { location, .. } 1365 1371 | Error::ExternalTypeWithConstructors { location, .. } 1366 1372 | Error::RecordUpdateVariantWithNoFields { location } 1373 + | Error::QualifiedTypeMissingName { location } 1367 1374 | Error::LowercaseBoolPattern { location } => location.start, 1368 1375 Error::UnknownLabels { unknown, .. } => { 1369 1376 unknown.iter().map(|(_, s)| s.start).min().unwrap_or(0) ··· 1558 1565 } 1559 1566 1560 1567 pub fn convert_get_type_constructor_error( 1561 - e: UnknownTypeConstructorError, 1568 + error: UnknownTypeConstructorError, 1562 1569 location: &SrcSpan, 1563 1570 module_location: Option<SrcSpan>, 1564 1571 ) -> Error { 1565 - match e { 1572 + match error { 1566 1573 UnknownTypeConstructorError::Type { name, hint } => Error::UnknownType { 1567 1574 location: *location, 1568 1575 name,
+37 -6
compiler-core/src/type_/hydrator.rs
··· 1 1 use super::*; 2 2 use crate::{ 3 3 analyse::name::check_name_case, 4 - ast::{Layer, TypeAst, TypeAstConstructor, TypeAstFn, TypeAstHole, TypeAstTuple, TypeAstVar}, 4 + ast::{ 5 + Layer, TypeAst, TypeAstConstructor, TypeAstConstructorName, TypeAstFn, TypeAstHole, 6 + TypeAstTuple, TypeAstVar, 7 + }, 5 8 reference::ReferenceKind, 6 9 }; 7 10 use std::sync::Arc; ··· 114 117 match ast { 115 118 TypeAst::Constructor(TypeAstConstructor { 116 119 location, 117 - name_location, 118 - module, 119 120 name, 120 121 arguments, 121 122 start_parentheses, ··· 127 128 argument_types.push((argument.location(), type_)); 128 129 } 129 130 131 + let module = match name { 132 + TypeAstConstructorName::Unqualified { .. } => None, 133 + TypeAstConstructorName::Qualified { 134 + module, 135 + module_location, 136 + .. 137 + } => Some((module.clone(), *module_location)), 138 + }; 139 + 140 + // If the constructor has been typed incorrectly with no name, 141 + // we can't figure out what type it's supposed to be, so it's 142 + // going to be an error: `wibble.` <- the name is missing! 143 + let (name, name_location) = match name { 144 + TypeAstConstructorName::Unqualified { name, location } 145 + | TypeAstConstructorName::Qualified { 146 + name: Some((name, location)), 147 + .. 148 + } => (name, location), 149 + TypeAstConstructorName::Qualified { 150 + name: None, 151 + module_location, 152 + dot_location, 153 + .. 154 + } => { 155 + return Err(Error::QualifiedTypeMissingName { 156 + location: SrcSpan::new(module_location.start, *dot_location), 157 + }); 158 + } 159 + }; 160 + 130 161 // Look up the constructor 131 162 let TypeConstructor { 132 163 parameters, ··· 134 165 deprecation, 135 166 .. 136 167 } = environment 137 - .get_type_constructor(module, name) 138 - .map_err(|e| { 168 + .get_type_constructor(&module, name) 169 + .map_err(|error| { 139 170 convert_get_type_constructor_error( 140 - e, 171 + error, 141 172 location, 142 173 module.as_ref().map(|(_, location)| *location), 143 174 )
+5
compiler-core/src/type_/tests/errors.rs
··· 3575 3575 " 3576 3576 ); 3577 3577 } 3578 + 3579 + #[test] 3580 + fn qualified_type_with_no_name_results_in_an_error() { 3581 + assert_module_error!("pub fn main() -> wibble. { todo }"); 3582 + }
+13
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__qualified_type_with_no_name_results_in_an_error.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "pub fn main() -> wibble. { todo }" 4 + --- 5 + ----- SOURCE CODE 6 + pub fn main() -> wibble. { todo } 7 + 8 + ----- ERROR 9 + error: Invalid type 10 + ┌─ /src/one/two.gleam:1:18 11 + 12 + 1 │ pub fn main() -> wibble. { todo } 13 + │ ^^^^^^^ This is not a valid type
+22 -56
language-server/src/code_action.rs
··· 8 8 self, ArgNames, AssignName, AssignmentKind, BitArraySegmentTruncation, BoundVariable, 9 9 BoundVariableName, CallArg, CustomType, FunctionLiteralKind, ImplicitCallArgOrigin, Import, 10 10 InvalidExpression, PIPE_PRECEDENCE, Pattern, PatternUnusedArguments, 11 - PipelineAssignmentKind, Publicity, RecordConstructor, SrcSpan, TodoKind, TypedArg, 12 - TypedAssignment, TypedClauseGuard, TypedDefinitions, TypedExpr, TypedFunction, 13 - TypedModuleConstant, TypedPattern, TypedPipelineAssignment, TypedRecordConstructor, 14 - TypedStatement, TypedTailPattern, TypedUse, visit::Visit as _, 11 + PipelineAssignmentKind, Publicity, RecordConstructor, SrcSpan, TodoKind, 12 + TypeAstConstructorName, TypedArg, TypedAssignment, TypedClauseGuard, TypedDefinitions, 13 + TypedExpr, TypedFunction, TypedModuleConstant, TypedPattern, TypedPipelineAssignment, 14 + TypedRecordConstructor, TypedStatement, TypedTailPattern, TypedUse, visit::Visit as _, 15 15 }, 16 16 build::{Located, Module}, 17 17 config::PackageConfig, ··· 1747 1747 fn visit_type_ast_constructor( 1748 1748 &mut self, 1749 1749 location: &'ast SrcSpan, 1750 - name_location: &'ast SrcSpan, 1751 - module: &'ast Option<(EcoString, SrcSpan)>, 1752 - name: &'ast EcoString, 1750 + name: &'ast TypeAstConstructorName, 1753 1751 arguments: &'ast [ast::TypeAst], 1754 1752 arguments_types: Option<Vec<Arc<Type>>>, 1755 1753 ) { 1756 1754 let range = src_span_to_lsp_range(*location, self.line_numbers); 1757 1755 if overlaps(self.params.range, range) 1758 - && let Some((module_alias, _)) = module 1756 + && let Some(module_alias) = name.module_name() 1757 + && let Some(name) = name.name() 1759 1758 && let Some(import) = self.get_module_import(module_alias, name, ast::Layer::Type) 1760 1759 { 1761 1760 self.qualified_constructor = Some(QualifiedConstructor { ··· 1765 1764 layer: ast::Layer::Type, 1766 1765 }); 1767 1766 } 1768 - ast::visit::visit_type_ast_constructor( 1769 - self, 1770 - location, 1771 - name_location, 1772 - module, 1773 - name, 1774 - arguments, 1775 - arguments_types, 1776 - ); 1767 + ast::visit::visit_type_ast_constructor(self, location, name, arguments, arguments_types); 1777 1768 } 1778 1769 1779 1770 fn visit_typed_expr_module_select( ··· 2000 1991 fn visit_type_ast_constructor( 2001 1992 &mut self, 2002 1993 location: &'ast SrcSpan, 2003 - name_location: &'ast SrcSpan, 2004 - module: &'ast Option<(EcoString, SrcSpan)>, 2005 - name: &'ast EcoString, 1994 + name: &'ast TypeAstConstructorName, 2006 1995 arguments: &'ast [ast::TypeAst], 2007 1996 arguments_types: Option<Vec<Arc<Type>>>, 2008 1997 ) { 2009 - if let Some((module_name, _)) = module { 1998 + if let Some(module_name) = name.module_name() 1999 + && let Some(name) = name.name() 2000 + { 2010 2001 let QualifiedConstructor { 2011 2002 used_name, 2012 2003 constructor, ··· 2018 2009 self.remove_module_qualifier(*location); 2019 2010 } 2020 2011 } 2021 - ast::visit::visit_type_ast_constructor( 2022 - self, 2023 - location, 2024 - name_location, 2025 - module, 2026 - name, 2027 - arguments, 2028 - arguments_types, 2029 - ); 2012 + ast::visit::visit_type_ast_constructor(self, location, name, arguments, arguments_types); 2030 2013 } 2031 2014 2032 2015 fn visit_typed_expr_module_select( ··· 2266 2249 fn visit_type_ast_constructor( 2267 2250 &mut self, 2268 2251 location: &'ast SrcSpan, 2269 - name_location: &'ast SrcSpan, 2270 - module: &'ast Option<(EcoString, SrcSpan)>, 2271 - name: &'ast EcoString, 2252 + name: &'ast TypeAstConstructorName, 2272 2253 arguments: &'ast [ast::TypeAst], 2273 2254 arguments_types: Option<Vec<Arc<Type>>>, 2274 2255 ) { 2275 - if module.is_none() 2256 + if !name.is_qualified() 2257 + && let Some(name) = name.name() 2276 2258 && overlaps( 2277 2259 self.params.range, 2278 2260 src_span_to_lsp_range(*location, self.line_numbers), ··· 2281 2263 self.get_module_import_from_type_constructor(name); 2282 2264 } 2283 2265 2284 - ast::visit::visit_type_ast_constructor( 2285 - self, 2286 - location, 2287 - name_location, 2288 - module, 2289 - name, 2290 - arguments, 2291 - arguments_types, 2292 - ); 2266 + ast::visit::visit_type_ast_constructor(self, location, name, arguments, arguments_types); 2293 2267 } 2294 2268 2295 2269 fn visit_typed_expr_var( ··· 2510 2484 fn visit_type_ast_constructor( 2511 2485 &mut self, 2512 2486 location: &'ast SrcSpan, 2513 - name_location: &'ast SrcSpan, 2514 - module: &'ast Option<(EcoString, SrcSpan)>, 2515 - name: &'ast EcoString, 2487 + name: &'ast TypeAstConstructorName, 2516 2488 arguments: &'ast [ast::TypeAst], 2517 2489 arguments_types: Option<Vec<Arc<Type>>>, 2518 2490 ) { 2519 - if module.is_none() { 2491 + if !name.is_qualified() 2492 + && let Some(name) = name.name() 2493 + { 2520 2494 let UnqualifiedConstructor { 2521 2495 constructor, layer, .. 2522 2496 } = &self.unqualified_constructor; ··· 2524 2498 self.add_module_qualifier(*location); 2525 2499 } 2526 2500 } 2527 - ast::visit::visit_type_ast_constructor( 2528 - self, 2529 - location, 2530 - name_location, 2531 - module, 2532 - name, 2533 - arguments, 2534 - arguments_types, 2535 - ); 2501 + ast::visit::visit_type_ast_constructor(self, location, name, arguments, arguments_types); 2536 2502 } 2537 2503 2538 2504 fn visit_typed_expr_var(
+12 -16
language-server/src/reference.rs
··· 7 7 analyse, 8 8 ast::{ 9 9 self, ArgNames, AssignName, BitArraySize, ClauseGuard, CustomType, Function, 10 - ModuleConstant, Pattern, RecordConstructor, SrcSpan, TypedExpr, TypedModule, visit::Visit, 10 + ModuleConstant, Pattern, RecordConstructor, SrcSpan, TypeAstConstructorName, TypedExpr, 11 + TypedModule, visit::Visit, 11 12 }, 12 13 build::Located, 13 14 type_::{ ··· 263 264 Some((module, name)) => { 264 265 let (target_kind, location) = match ast { 265 266 ast::TypeAst::Constructor(constructor) => { 266 - let kind = if constructor.module.is_some() { 267 + let kind = if constructor.name.is_qualified() { 267 268 RenameTarget::Qualified 268 269 } else { 269 270 RenameTarget::Unqualified 270 271 }; 271 - (kind, constructor.name_location) 272 + let name_location = constructor.name.name_location()?; 273 + (kind, name_location) 272 274 } 273 275 ast::TypeAst::Fn(_) 274 276 | ast::TypeAst::Var(_) ··· 917 919 fn visit_type_ast_constructor( 918 920 &mut self, 919 921 location: &'ast SrcSpan, 920 - name_location: &'ast SrcSpan, 921 - module: &'ast Option<(EcoString, SrcSpan)>, 922 - name: &'ast EcoString, 922 + name: &'ast TypeAstConstructorName, 923 923 arguments: &'ast [ast::TypeAst], 924 924 arguments_types: Option<Vec<std::sync::Arc<Type>>>, 925 925 ) { 926 - if let Some((module_alias, module_location)) = module 926 + if let TypeAstConstructorName::Qualified { 927 + module: module_alias, 928 + module_location, 929 + .. 930 + } = name 927 931 && module_alias == self.module_alias 928 932 { 929 933 self.references.push(ModuleNameReference { ··· 932 936 }) 933 937 } 934 938 935 - ast::visit::visit_type_ast_constructor( 936 - self, 937 - location, 938 - name_location, 939 - module, 940 - name, 941 - arguments, 942 - arguments_types, 943 - ); 939 + ast::visit::visit_type_ast_constructor(self, location, name, arguments, arguments_types); 944 940 } 945 941 946 942 fn visit_typed_constant_record(
+65
language-server/src/tests/completion.rs
··· 2424 2424 Position::new(1, 17) 2425 2425 ); 2426 2426 } 2427 + 2428 + const OPTION_MODULE: &str = " 2429 + pub type Option(a) { Some(a) None } 2430 + pub fn all() {} 2431 + "; 2432 + 2433 + #[test] 2434 + fn do_not_show_completions_for_module_values_when_typing_an_argument_type() { 2435 + assert_completion!( 2436 + TestProject::for_source( 2437 + " 2438 + import option 2439 + pub fn main(n: option.) {} 2440 + " 2441 + ) 2442 + .add_dep_module("option", OPTION_MODULE), 2443 + Position::new(2, 22) 2444 + ); 2445 + } 2446 + 2447 + #[test] 2448 + fn do_not_show_completions_for_module_values_when_typing_a_return_type() { 2449 + assert_completion!( 2450 + TestProject::for_source( 2451 + " 2452 + import option 2453 + pub fn main() -> option. {} 2454 + " 2455 + ) 2456 + .add_dep_module("option", OPTION_MODULE), 2457 + Position::new(2, 24) 2458 + ); 2459 + } 2460 + 2461 + #[test] 2462 + fn do_not_show_completions_for_module_values_when_typing_a_constructor_type() { 2463 + assert_completion!( 2464 + TestProject::for_source( 2465 + " 2466 + import option 2467 + pub type Wibble { 2468 + Wibble(option.) 2469 + } 2470 + " 2471 + ) 2472 + .add_dep_module("option", OPTION_MODULE), 2473 + Position::new(3, 16) 2474 + ); 2475 + } 2476 + 2477 + #[test] 2478 + fn do_not_show_completions_for_module_values_when_typing_an_annotation() { 2479 + assert_completion!( 2480 + TestProject::for_source( 2481 + " 2482 + import option 2483 + pub fn main() { 2484 + let a: option. = todo 2485 + } 2486 + " 2487 + ) 2488 + .add_dep_module("option", OPTION_MODULE), 2489 + Position::new(3, 16) 2490 + ); 2491 + }
+17
language-server/src/tests/snapshots/gleam_language_server__tests__completion__do_not_show_completions_for_module_values_when_typing_a_constructor_type.snap
··· 1 + --- 2 + source: language-server/src/tests/completion.rs 3 + expression: "\nimport option\npub type Wibble {\n Wibble(option.)\n}\n" 4 + --- 5 + import option 6 + pub type Wibble { 7 + Wibble(option.|) 8 + } 9 + 10 + 11 + ----- Completion content ----- 12 + option.Option 13 + kind: Class 14 + detail: Type 15 + sort: 4_option.Option 16 + edits: 17 + [3:9-3:16]: "option.Option"
+15
language-server/src/tests/snapshots/gleam_language_server__tests__completion__do_not_show_completions_for_module_values_when_typing_a_return_type.snap
··· 1 + --- 2 + source: language-server/src/tests/completion.rs 3 + expression: "\nimport option\npub fn main() -> option. {}\n" 4 + --- 5 + import option 6 + pub fn main() -> option.| {} 7 + 8 + 9 + ----- Completion content ----- 10 + option.Option 11 + kind: Class 12 + detail: Type 13 + sort: 4_option.Option 14 + edits: 15 + [2:17-2:24]: "option.Option"
+17
language-server/src/tests/snapshots/gleam_language_server__tests__completion__do_not_show_completions_for_module_values_when_typing_an_annotation.snap
··· 1 + --- 2 + source: language-server/src/tests/completion.rs 3 + expression: "\nimport option\npub fn main() {\n let a: option. = todo\n}\n" 4 + --- 5 + import option 6 + pub fn main() { 7 + let a: option.| = todo 8 + } 9 + 10 + 11 + ----- Completion content ----- 12 + option.Option 13 + kind: Class 14 + detail: Type 15 + sort: 4_option.Option 16 + edits: 17 + [3:9-3:16]: "option.Option"
+15
language-server/src/tests/snapshots/gleam_language_server__tests__completion__do_not_show_completions_for_module_values_when_typing_an_argument_type.snap
··· 1 + --- 2 + source: language-server/src/tests/completion.rs 3 + expression: "\nimport option\npub fn main(n: option.) {}\n" 4 + --- 5 + import option 6 + pub fn main(n: option.|) {} 7 + 8 + 9 + ----- Completion content ----- 10 + option.Option 11 + kind: Class 12 + detail: Type 13 + sort: 4_option.Option 14 + edits: 15 + [2:15-2:22]: "option.Option"