@@ -36,8 +36,8 @@ struct ArrowAnalysis {
3636 decor_role : Option < EndpointRole > ,
3737}
3838
39- struct RelationValidationInput {
40- relation : Relation ,
39+ struct RelationValidationInput < ' a > {
40+ relation : & ' a Relation ,
4141 has_interface_tokens : bool ,
4242 src_is_interface : bool ,
4343 tgt_is_interface : bool ,
@@ -46,7 +46,7 @@ struct RelationValidationInput {
4646 src_port_role : Option < EndpointRole > ,
4747}
4848
49- type RelationValidationRule = fn ( & RelationValidationInput ) -> Option < ElementResolverError > ;
49+ type RelationValidationRule = fn ( & RelationValidationInput < ' _ > ) -> Option < ElementResolverError > ;
5050
5151#[ derive( Default ) ]
5252pub struct ElementResolver {
@@ -110,8 +110,11 @@ impl ElementResolver {
110110 // and whose parent element is a descendant of (or equal to) the current scope.
111111 let scope_prefix = scope. join ( "." ) ;
112112 for ( pfqn, parent_comp) in & self . port_parents {
113- let parts: Vec < & str > = pfqn. split ( '.' ) . collect ( ) ;
114- if parts. last ( ) != Some ( & port_local) {
113+ let pfqn_last = match pfqn. rfind ( '.' ) {
114+ Some ( i) => & pfqn[ i + 1 ..] ,
115+ None => pfqn,
116+ } ;
117+ if pfqn_last != port_local {
115118 continue ;
116119 }
117120
@@ -169,18 +172,22 @@ impl ElementResolver {
169172 Some ( scope. join ( "." ) )
170173 } ;
171174
172- let children: Vec < String > = self
175+ let children: & [ String ] = self
173176 . child_elements_by_parent
174177 . get ( & scope_key)
175- . cloned ( )
176- . unwrap_or_default ( ) ;
178+ . map ( Vec :: as_slice )
179+ . unwrap_or ( & [ ] ) ;
177180
178181 for child_id in children {
179- let Some ( element) = self . elements . get ( & child_id) else {
182+ let Some ( element) = self . elements . get ( child_id) else {
180183 continue ;
181184 } ;
182185
183- let Some ( name) = element. alias . as_deref ( ) . or_else ( || element. name . as_deref ( ) ) else {
186+ let Some ( name) = element
187+ . alias
188+ . as_deref ( )
189+ . or_else ( || element. name . as_deref ( ) )
190+ else {
184191 continue ;
185192 } ;
186193
@@ -253,6 +260,8 @@ impl ElementResolver {
253260 . unwrap_or ( false ) ;
254261
255262 if ok {
263+ // Invariant: after aligning candidates to resolved endpoint identity, there should be at most one match.
264+ // If multiple matches remain, this indicates inconsistent resolver state or unexpected duplicate-alignment; fail fast with AmbiguousReference instead of silently picking one.
256265 if matched. is_some ( ) {
257266 return Err ( ElementResolverError :: AmbiguousReference {
258267 reference : raw. to_string ( ) ,
@@ -504,7 +513,7 @@ impl ElementResolver {
504513 src_port_role : Option < EndpointRole > ,
505514 ) -> Result < ( ) , ElementResolverError > {
506515 let input = RelationValidationInput {
507- relation : relation . clone ( ) ,
516+ relation,
508517 has_interface_tokens,
509518 src_is_interface,
510519 tgt_is_interface,
@@ -531,7 +540,7 @@ impl ElementResolver {
531540 }
532541
533542 fn rule_require_exactly_one_interface_endpoint (
534- input : & RelationValidationInput ,
543+ input : & RelationValidationInput < ' _ >
535544 ) -> Option < ElementResolverError > {
536545 if input. has_interface_tokens
537546 && !input. src_is_interface
@@ -549,7 +558,7 @@ impl ElementResolver {
549558 }
550559
551560 fn rule_disallow_interface_to_interface (
552- input : & RelationValidationInput ,
561+ input : & RelationValidationInput < ' _ >
553562 ) -> Option < ElementResolverError > {
554563 if input. has_interface_tokens
555564 && input. src_is_interface
@@ -567,7 +576,7 @@ impl ElementResolver {
567576 }
568577
569578 fn rule_require_component_endpoint_for_binding (
570- input : & RelationValidationInput ,
579+ input : & RelationValidationInput < ' _ >
571580 ) -> Option < ElementResolverError > {
572581 if input. has_interface_tokens && input. decor_role . is_some ( ) {
573582 if !input. src_is_component || !input. tgt_is_interface {
@@ -582,7 +591,7 @@ impl ElementResolver {
582591 }
583592
584593 fn rule_disallow_generic_decor_with_direction (
585- input : & RelationValidationInput ,
594+ input : & RelationValidationInput < ' _ >
586595 ) -> Option < ElementResolverError > {
587596 if input. has_interface_tokens
588597 && input. decor_role . is_none ( )
@@ -598,9 +607,11 @@ impl ElementResolver {
598607 None
599608 }
600609
601- fn rule_port_role_consistency ( input : & RelationValidationInput ) -> Option < ElementResolverError > {
610+ fn rule_port_role_consistency (
611+ input : & RelationValidationInput < ' _ >
612+ ) -> Option < ElementResolverError > {
602613 if let ( Some ( port_role) , Some ( decor_role) ) =
603- ( input. src_port_role . clone ( ) , input. decor_role . clone ( ) )
614+ ( input. src_port_role , input. decor_role )
604615 {
605616 if port_role != decor_role {
606617 return Some ( ElementResolverError :: InvalidRelationship {
@@ -641,35 +652,30 @@ impl ElementResolver {
641652 src_is_interface,
642653 tgt_is_interface,
643654 src_is_component,
644- parsed_arrow. decor_role . clone ( ) ,
645- src_port_role. clone ( ) ,
655+ parsed_arrow. decor_role ,
656+ src_port_role,
646657 ) ?;
647658
648659 let relation_type = Self :: infer_relation_type ( & parsed_arrow) ;
649- let src_role = src_port_role. clone ( ) ;
650- let tgt_role = tgt_port_role. clone ( ) ;
651660
652661 let source_role = if relation_type == ComponentRelationType :: InterfaceBinding {
662+ // Guard-only invariant check: InterfaceBinding should always carry a decorator role.
663+ // If this panics, resolver invariants have been broken by upstream logic changes.
653664 parsed_arrow
654665 . decor_role
655- . clone ( )
656- . or ( src_role)
657- . or ( tgt_role)
658- . unwrap_or ( EndpointRole :: None )
666+ . expect ( "Invariant: InterfaceBinding requires decorator role" )
659667 } else {
660668 EndpointRole :: None
661669 } ;
662670
663- let ( owner_fqn, target_fqn) = ( src_fqn. clone ( ) , tgt_fqn. clone ( ) ) ;
664-
665- let source_element = self . elements . get_mut ( & owner_fqn) . ok_or_else ( || {
671+ let source_element = self . elements . get_mut ( & src_fqn) . ok_or_else ( || {
666672 ElementResolverError :: UnresolvedReference {
667- reference : owner_fqn . clone ( ) ,
673+ reference : src_fqn . clone ( ) ,
668674 }
669675 } ) ?;
670676
671677 let duplicate = source_element. relations . iter ( ) . any ( |existing| {
672- existing. target == target_fqn
678+ existing. target == tgt_fqn
673679 && existing. relation_type == relation_type
674680 && existing. source_role == source_role
675681 } ) ;
@@ -679,7 +685,7 @@ impl ElementResolver {
679685 }
680686
681687 source_element. relations . push ( LogicRelation {
682- target : target_fqn ,
688+ target : tgt_fqn ,
683689 annotation : relation. description . clone ( ) ,
684690 relation_type,
685691 source_role,
0 commit comments