@@ -630,9 +630,29 @@ impl<'a> Resolver<'a> {
630630
631631 let mut export_spans = Vec :: new ( ) ;
632632 let mut import_spans = Vec :: new ( ) ;
633+ // Track case-normalized names to catch case-insensitive duplicates
634+ let mut import_names: HashMap < String , ( String , Span ) > = HashMap :: new ( ) ;
635+ let mut export_names: HashMap < String , ( String , Span ) > = HashMap :: new ( ) ;
636+
633637 for ( name, ( item, span) ) in self . type_lookup . iter ( ) {
634638 match * item {
635639 TypeOrItem :: Type ( id) => {
640+ // Check for case-insensitive duplicate
641+ let lowercase_name = name. to_lowercase ( ) ;
642+ if let Some ( ( existing_name, _existing_span) ) = import_names. get ( & lowercase_name)
643+ {
644+ // Only error on case-insensitive duplicates (e.g., "foo" vs "FOO").
645+ // Exact duplicates fall through to the existing check below.
646+ if existing_name != name {
647+ bail ! ( Error :: new(
648+ * span,
649+ format!(
650+ "import `{name}` conflicts with import `{existing_name}` (kebab-case identifiers are case-insensitive)"
651+ ) ,
652+ ) )
653+ }
654+ }
655+
636656 let prev = self . worlds [ world_id]
637657 . imports
638658 . insert ( WorldKey :: Name ( name. to_string ( ) ) , WorldItem :: Type ( id) ) ;
@@ -642,6 +662,7 @@ impl<'a> Resolver<'a> {
642662 format!( "import `{name}` conflicts with prior import of same name" ) ,
643663 ) )
644664 }
665+ import_names. insert ( lowercase_name, ( name. to_string ( ) , * span) ) ;
645666 import_spans. push ( * span) ;
646667 }
647668 TypeOrItem :: Item ( _) => unreachable ! ( ) ,
@@ -674,16 +695,36 @@ impl<'a> Resolver<'a> {
674695 ty : ast:: Type :: Resource ( r) ,
675696 ..
676697 } ) => {
677- for func in r. funcs . iter ( ) {
678- import_spans. push ( func. named_func ( ) . name . span ) ;
679- let func = self . resolve_resource_func ( func, name) ?;
680- let prev = self . worlds [ world_id]
681- . imports
682- . insert ( WorldKey :: Name ( func. name . clone ( ) ) , WorldItem :: Function ( func) ) ;
698+ for ast_func in r. funcs . iter ( ) {
699+ let func_span = ast_func. named_func ( ) . name . span ;
700+ import_spans. push ( func_span) ;
701+ let func = self . resolve_resource_func ( ast_func, name) ?;
702+
703+ // Check for case-insensitive duplicate
704+ let lowercase_name = func. name . to_lowercase ( ) ;
705+ if let Some ( ( existing_name, _existing_span) ) =
706+ import_names. get ( & lowercase_name)
707+ {
708+ if existing_name != & func. name {
709+ bail ! ( Error :: new(
710+ func_span,
711+ format!(
712+ "import `{}` conflicts with import `{existing_name}` (kebab-case identifiers are case-insensitive)" ,
713+ func. name
714+ ) ,
715+ ) )
716+ }
717+ }
718+
719+ let prev = self . worlds [ world_id] . imports . insert (
720+ WorldKey :: Name ( func. name . clone ( ) ) ,
721+ WorldItem :: Function ( func. clone ( ) ) ,
722+ ) ;
683723 // Resource names themselves are unique, and methods are
684724 // uniquely named, so this should be possible to assert
685725 // at this point and never trip.
686726 assert ! ( prev. is_none( ) ) ;
727+ import_names. insert ( lowercase_name, ( func. name . clone ( ) , func_span) ) ;
687728 }
688729 continue ;
689730 }
@@ -724,6 +765,30 @@ impl<'a> Resolver<'a> {
724765 ) )
725766 }
726767 }
768+
769+ // Check for case-insensitive duplicate names.
770+ if let WorldKey :: Name ( ref name) = key {
771+ let lowercase_name = name. to_lowercase ( ) ;
772+ let names = if desc == "import" {
773+ & mut import_names
774+ } else {
775+ & mut export_names
776+ } ;
777+
778+ if let Some ( ( existing_name, _existing_span) ) = names. get ( & lowercase_name) {
779+ // Only error on case-insensitive duplicates (e.g., "foo" vs "FOO").
780+ if existing_name != name {
781+ bail ! ( Error :: new(
782+ kind. span( ) ,
783+ format!(
784+ "{desc} `{name}` conflicts with {desc} `{existing_name}` (kebab-case identifiers are case-insensitive)"
785+ ) ,
786+ ) )
787+ }
788+ }
789+ names. insert ( lowercase_name, ( name. clone ( ) , kind. span ( ) ) ) ;
790+ }
791+
727792 let dst = if desc == "import" {
728793 & mut self . worlds [ world_id] . imports
729794 } else {
0 commit comments