@@ -34,11 +34,11 @@ pub use location::{
3434 AliasDeclarationLocation , AliasLocation , AliasMemberLocation , AttributeMemberLocation ,
3535 ClassDeclarationLocation , ClassInstanceLocation , ClassSingletonLocation , ClassSuperLocation ,
3636 ConstantDeclarationLocation , FunctionParamLocation , GlobalDeclarationLocation ,
37- InterfaceDeclarationLocation , InterfaceLocation , LocationRange , MethodDefinitionLocation ,
38- MethodTypeLocation , MixinMemberLocation , ModuleDeclarationLocation , ModuleSelfLocation ,
39- ResolveTypeNamesDirectiveLocation , TypeAliasDeclarationLocation , TypeParamLocation ,
40- UseDirectiveLocation , UseSingleClauseLocation , UseWildcardClauseLocation ,
41- VariableMemberLocation ,
37+ InterfaceDeclarationLocation , InterfaceLocation , KeywordParamLocation , LocationRange ,
38+ MethodDefinitionLocation , MethodTypeLocation , MixinMemberLocation , ModuleDeclarationLocation ,
39+ ModuleSelfLocation , RecordFieldLocation , ResolveTypeNamesDirectiveLocation ,
40+ TypeAliasDeclarationLocation , TypeParamLocation , UseDirectiveLocation , UseSingleClauseLocation ,
41+ UseWildcardClauseLocation , VariableMemberLocation ,
4242} ;
4343pub use members:: {
4444 AliasKind , AliasMember , AttrAccessorMember , AttrReaderMember , AttrWriterMember , AttributeKind ,
@@ -58,8 +58,9 @@ pub use types::{
5858#[ cfg( test) ]
5959mod tests {
6060 use crate :: ast:: {
61- AstConverter , BaseType , BaseTypeKind , ClassMember , Declaration , Directive , IvarName ,
62- Literal , Member , MethodKind , ModuleMember , RecordKey , Type , UseClause ,
61+ AstConverter , BaseType , BaseTypeKind , ClassMember , Declaration , Directive , Function ,
62+ IvarName , Literal , LocationRange , Member , MethodKind , ModuleMember , RecordKey , Type ,
63+ UseClause ,
6364 } ;
6465 use crate :: interner:: StringInterner ;
6566 use crate :: node:: { Node , parse} ;
@@ -324,4 +325,101 @@ mod tests {
324325 assert_eq ! ( type_names. display( wildcard. namespace, & strings) , "Foo::Baz" ) ;
325326 assert ! ( wildcard. location. is_some( ) ) ;
326327 }
328+
329+ #[ test]
330+ fn converts_keyword_param_locations ( ) {
331+ let source = "class Foo\n def bar: (name: String, ?size: Integer bytes) -> void\n end\n " ;
332+ let signature = parse ( source) . unwrap ( ) ;
333+
334+ let mut strings = StringInterner :: new ( ) ;
335+ let mut type_names = TypeNameInterner :: new ( ) ;
336+ let mut converter = AstConverter :: new ( & mut strings, & mut type_names) ;
337+ let declaration =
338+ converter. convert_declaration ( & signature. declarations ( ) . iter ( ) . next ( ) . unwrap ( ) ) ;
339+
340+ let Declaration :: Class ( class_decl) = & declaration else {
341+ panic ! ( "expected class declaration" ) ;
342+ } ;
343+ let ClassMember :: Member ( Member :: MethodDefinition ( method) ) = & class_decl. members [ 0 ] else {
344+ panic ! ( "expected method definition member" ) ;
345+ } ;
346+ let Function :: Typed ( function) = & method. overloads [ 0 ] . method_type . function else {
347+ panic ! ( "expected typed function" ) ;
348+ } ;
349+
350+ let text =
351+ |range : & LocationRange | & source[ range. start_byte as usize ..range. end_byte as usize ] ;
352+
353+ let required = & function. required_keywords [ 0 ] ;
354+ let required_location = required. location . as_ref ( ) . unwrap ( ) ;
355+ assert_eq ! ( required. name, strings. intern( "name" ) ) ;
356+ assert_eq ! ( text( & required_location. range) , "name: String" ) ;
357+ assert_eq ! ( text( & required_location. name_range) , "name" ) ;
358+
359+ let optional = & function. optional_keywords [ 0 ] ;
360+ let optional_location = optional. location . as_ref ( ) . unwrap ( ) ;
361+ assert_eq ! ( optional. name, strings. intern( "size" ) ) ;
362+ assert_eq ! ( text( & optional_location. range) , "size: Integer bytes" ) ;
363+ assert_eq ! ( text( & optional_location. name_range) , "size" ) ;
364+ }
365+
366+ #[ test]
367+ fn converts_record_field_locations ( ) {
368+ let source = "type t = { name: String, ?age: Integer, \" id\" => Integer }\n " ;
369+ let signature = parse ( source) . unwrap ( ) ;
370+
371+ let mut strings = StringInterner :: new ( ) ;
372+ let mut type_names = TypeNameInterner :: new ( ) ;
373+ let mut converter = AstConverter :: new ( & mut strings, & mut type_names) ;
374+ let declaration =
375+ converter. convert_declaration ( & signature. declarations ( ) . iter ( ) . next ( ) . unwrap ( ) ) ;
376+
377+ let Declaration :: TypeAlias ( alias) = & declaration else {
378+ panic ! ( "expected type alias declaration" ) ;
379+ } ;
380+ let Type :: Record ( record) = & alias. ty else {
381+ panic ! ( "expected record type" ) ;
382+ } ;
383+
384+ let text =
385+ |range : & LocationRange | & source[ range. start_byte as usize ..range. end_byte as usize ] ;
386+ let location = |index : usize | record. fields [ index] . location . as_ref ( ) . unwrap ( ) ;
387+
388+ assert_eq ! ( text( & location( 0 ) . range) , "name: String" ) ;
389+ assert_eq ! ( text( & location( 0 ) . key_range) , "name" ) ;
390+
391+ // The `?` marker belongs to the record type, not to the field.
392+ assert_eq ! ( text( & location( 1 ) . range) , "age: Integer" ) ;
393+ assert_eq ! ( text( & location( 1 ) . key_range) , "age" ) ;
394+
395+ assert_eq ! ( text( & location( 2 ) . range) , "\" id\" => Integer" ) ;
396+ assert_eq ! ( text( & location( 2 ) . key_range) , "\" id\" " ) ;
397+ }
398+
399+ #[ test]
400+ fn converts_method_definition_overload_locations ( ) {
401+ let source = "class Foo\n def bar: () -> void\n | (Integer) -> String\n end\n " ;
402+ let signature = parse ( source) . unwrap ( ) ;
403+
404+ let mut strings = StringInterner :: new ( ) ;
405+ let mut type_names = TypeNameInterner :: new ( ) ;
406+ let mut converter = AstConverter :: new ( & mut strings, & mut type_names) ;
407+ let declaration =
408+ converter. convert_declaration ( & signature. declarations ( ) . iter ( ) . next ( ) . unwrap ( ) ) ;
409+
410+ let Declaration :: Class ( class_decl) = & declaration else {
411+ panic ! ( "expected class declaration" ) ;
412+ } ;
413+ let ClassMember :: Member ( Member :: MethodDefinition ( method) ) = & class_decl. members [ 0 ] else {
414+ panic ! ( "expected method definition member" ) ;
415+ } ;
416+
417+ let text =
418+ |range : & LocationRange | & source[ range. start_byte as usize ..range. end_byte as usize ] ;
419+ let location = |index : usize | method. overloads [ index] . location . as_ref ( ) . unwrap ( ) ;
420+
421+ // The leading `:`/`|` separator is part of the overload range.
422+ assert_eq ! ( text( location( 0 ) ) , ": () -> void" ) ;
423+ assert_eq ! ( text( location( 1 ) ) , "| (Integer) -> String" ) ;
424+ }
327425}
0 commit comments