1010//! - **Feature group connection expansion tracking**: Reports diagnostics for
1111//! feature group connections that could not be expanded.
1212
13- use spar_hir_def:: feature_group:: { expand_feature_group , flip_direction , ExpandedFeature } ;
13+ use spar_hir_def:: feature_group:: { ExpandedFeature , expand_feature_group , flip_direction } ;
1414use spar_hir_def:: instance:: SystemInstance ;
1515use spar_hir_def:: item_tree:: { ConnectionKind , Direction , FeatureKind , ItemTree } ;
1616use spar_hir_def:: name:: Name ;
@@ -233,12 +233,10 @@ pub fn check_feature_group_complements(
233233 } ;
234234
235235 // Expand the feature groups.
236- let src_expanded = expand_component_feature_group (
237- instance, scope, src_comp_idx, & src_end. feature ,
238- ) ;
239- let dst_expanded = expand_component_feature_group (
240- instance, scope, dst_comp_idx, & dst_end. feature ,
241- ) ;
236+ let src_expanded =
237+ expand_component_feature_group ( instance, scope, src_comp_idx, & src_end. feature ) ;
238+ let dst_expanded =
239+ expand_component_feature_group ( instance, scope, dst_comp_idx, & dst_end. feature ) ;
242240
243241 let ( src_features, dst_features) = match ( src_expanded, dst_expanded) {
244242 ( Some ( s) , Some ( d) ) => ( s, d) ,
@@ -271,9 +269,15 @@ pub fn check_feature_group_complements(
271269 (source: {}, destination: {}, expected destination: {})",
272270 conn. name,
273271 mismatch. feature_name,
274- mismatch. source_direction. map_or( "none" . to_string( ) , |d| d. to_string( ) ) ,
275- mismatch. destination_direction. map_or( "none" . to_string( ) , |d| d. to_string( ) ) ,
276- mismatch. source_direction. map_or( "none" . to_string( ) , |d| flip_direction( d) . to_string( ) ) ,
272+ mismatch
273+ . source_direction
274+ . map_or( "none" . to_string( ) , |d| d. to_string( ) ) ,
275+ mismatch
276+ . destination_direction
277+ . map_or( "none" . to_string( ) , |d| d. to_string( ) ) ,
278+ mismatch
279+ . source_direction
280+ . map_or( "none" . to_string( ) , |d| flip_direction( d) . to_string( ) ) ,
277281 ) ,
278282 path : conn_path. clone ( ) ,
279283 analysis : "feature_group_check" . to_string ( ) ,
@@ -295,9 +299,11 @@ fn resolve_endpoint_component(
295299 match subcomponent {
296300 Some ( sub_name) => {
297301 let owner_comp = instance. component ( owner) ;
298- owner_comp. children . iter ( ) . find ( |& & child_idx| {
299- instance. component ( child_idx) . name . eq_ci ( sub_name)
300- } ) . copied ( )
302+ owner_comp
303+ . children
304+ . iter ( )
305+ . find ( |& & child_idx| instance. component ( child_idx) . name . eq_ci ( sub_name) )
306+ . copied ( )
301307 }
302308 None => Some ( owner) ,
303309 }
@@ -457,8 +463,14 @@ mod tests {
457463 ] ;
458464
459465 let result = validate_complement ( & source, & destination) ;
460- assert ! ( result. unmatched_source. is_empty( ) , "all features should match" ) ;
461- assert ! ( result. direction_mismatches. is_empty( ) , "directions should be complementary" ) ;
466+ assert ! (
467+ result. unmatched_source. is_empty( ) ,
468+ "all features should match"
469+ ) ;
470+ assert ! (
471+ result. direction_mismatches. is_empty( ) ,
472+ "directions should be complementary"
473+ ) ;
462474 }
463475
464476 #[ test]
@@ -477,14 +489,12 @@ mod tests {
477489 group_prefix: None ,
478490 } ,
479491 ] ;
480- let destination = vec ! [
481- ExpandedFeature {
482- name: Name :: new( "temp" ) ,
483- kind: FeatureKind :: DataPort ,
484- direction: Some ( Direction :: In ) ,
485- group_prefix: None ,
486- } ,
487- ] ;
492+ let destination = vec ! [ ExpandedFeature {
493+ name: Name :: new( "temp" ) ,
494+ kind: FeatureKind :: DataPort ,
495+ direction: Some ( Direction :: In ) ,
496+ group_prefix: None ,
497+ } ] ;
488498
489499 let result = validate_complement ( & source, & destination) ;
490500 assert_eq ! ( result. unmatched_source. len( ) , 1 ) ;
@@ -493,22 +503,18 @@ mod tests {
493503
494504 #[ test]
495505 fn complement_direction_mismatch ( ) {
496- let source = vec ! [
497- ExpandedFeature {
498- name: Name :: new( "data" ) ,
499- kind: FeatureKind :: DataPort ,
500- direction: Some ( Direction :: Out ) ,
501- group_prefix: None ,
502- } ,
503- ] ;
504- let destination = vec ! [
505- ExpandedFeature {
506- name: Name :: new( "data" ) ,
507- kind: FeatureKind :: DataPort ,
508- direction: Some ( Direction :: Out ) , // Should be In!
509- group_prefix: None ,
510- } ,
511- ] ;
506+ let source = vec ! [ ExpandedFeature {
507+ name: Name :: new( "data" ) ,
508+ kind: FeatureKind :: DataPort ,
509+ direction: Some ( Direction :: Out ) ,
510+ group_prefix: None ,
511+ } ] ;
512+ let destination = vec ! [ ExpandedFeature {
513+ name: Name :: new( "data" ) ,
514+ kind: FeatureKind :: DataPort ,
515+ direction: Some ( Direction :: Out ) , // Should be In!
516+ group_prefix: None ,
517+ } ] ;
512518
513519 let result = validate_complement ( & source, & destination) ;
514520 assert ! ( result. unmatched_source. is_empty( ) ) ;
@@ -518,48 +524,46 @@ mod tests {
518524
519525 #[ test]
520526 fn complement_inout_matches_inout ( ) {
521- let source = vec ! [
522- ExpandedFeature {
523- name: Name :: new( "bus" ) ,
524- kind: FeatureKind :: DataPort ,
525- direction: Some ( Direction :: InOut ) ,
526- group_prefix: None ,
527- } ,
528- ] ;
529- let destination = vec ! [
530- ExpandedFeature {
531- name: Name :: new( "bus" ) ,
532- kind: FeatureKind :: DataPort ,
533- direction: Some ( Direction :: InOut ) ,
534- group_prefix: None ,
535- } ,
536- ] ;
527+ let source = vec ! [ ExpandedFeature {
528+ name: Name :: new( "bus" ) ,
529+ kind: FeatureKind :: DataPort ,
530+ direction: Some ( Direction :: InOut ) ,
531+ group_prefix: None ,
532+ } ] ;
533+ let destination = vec ! [ ExpandedFeature {
534+ name: Name :: new( "bus" ) ,
535+ kind: FeatureKind :: DataPort ,
536+ direction: Some ( Direction :: InOut ) ,
537+ group_prefix: None ,
538+ } ] ;
537539
538540 let result = validate_complement ( & source, & destination) ;
539- assert ! ( result. direction_mismatches. is_empty( ) , "InOut matches InOut" ) ;
541+ assert ! (
542+ result. direction_mismatches. is_empty( ) ,
543+ "InOut matches InOut"
544+ ) ;
540545 }
541546
542547 #[ test]
543548 fn complement_case_insensitive_matching ( ) {
544- let source = vec ! [
545- ExpandedFeature {
546- name: Name :: new( "Temperature" ) ,
547- kind: FeatureKind :: DataPort ,
548- direction: Some ( Direction :: Out ) ,
549- group_prefix: None ,
550- } ,
551- ] ;
552- let destination = vec ! [
553- ExpandedFeature {
554- name: Name :: new( "temperature" ) ,
555- kind: FeatureKind :: DataPort ,
556- direction: Some ( Direction :: In ) ,
557- group_prefix: None ,
558- } ,
559- ] ;
549+ let source = vec ! [ ExpandedFeature {
550+ name: Name :: new( "Temperature" ) ,
551+ kind: FeatureKind :: DataPort ,
552+ direction: Some ( Direction :: Out ) ,
553+ group_prefix: None ,
554+ } ] ;
555+ let destination = vec ! [ ExpandedFeature {
556+ name: Name :: new( "temperature" ) ,
557+ kind: FeatureKind :: DataPort ,
558+ direction: Some ( Direction :: In ) ,
559+ group_prefix: None ,
560+ } ] ;
560561
561562 let result = validate_complement ( & source, & destination) ;
562- assert ! ( result. unmatched_source. is_empty( ) , "matching should be case-insensitive" ) ;
563+ assert ! (
564+ result. unmatched_source. is_empty( ) ,
565+ "matching should be case-insensitive"
566+ ) ;
563567 assert ! ( result. direction_mismatches. is_empty( ) ) ;
564568 }
565569
@@ -601,7 +605,11 @@ mod tests {
601605 } ) ;
602606
603607 let diags = check_inverse_of_rules ( & tree) ;
604- assert ! ( diags. is_empty( ) , "inverse_of with no features should be valid: {:?}" , diags) ;
608+ assert ! (
609+ diags. is_empty( ) ,
610+ "inverse_of with no features should be valid: {:?}" ,
611+ diags
612+ ) ;
605613 }
606614
607615 #[ test]
@@ -828,26 +836,45 @@ mod tests {
828836
829837 // The instance should have semantic connections from FG expansion.
830838 // We should find individual semantic connections for "temp" and "pressure".
831- let fg_semantic: Vec < _ > = instance. semantic_connections . iter ( )
839+ let fg_semantic: Vec < _ > = instance
840+ . semantic_connections
841+ . iter ( )
832842 . filter ( |sc| sc. name . as_str ( ) . starts_with ( "c1." ) )
833843 . collect ( ) ;
834844
835845 assert_eq ! (
836- fg_semantic. len( ) , 2 ,
846+ fg_semantic. len( ) ,
847+ 2 ,
837848 "feature group connection should expand into 2 individual connections, \
838849 got {} semantic connections: {:?}",
839850 fg_semantic. len( ) ,
840- instance. semantic_connections. iter( ) . map( |sc| sc. name. as_str( ) ) . collect:: <Vec <_>>( )
851+ instance
852+ . semantic_connections
853+ . iter( )
854+ . map( |sc| sc. name. as_str( ) )
855+ . collect:: <Vec <_>>( )
841856 ) ;
842857
843858 // Check that we have connections for both temp and pressure
844859 let names: Vec < _ > = fg_semantic. iter ( ) . map ( |sc| sc. name . as_str ( ) ) . collect ( ) ;
845- assert ! ( names. contains( & "c1.temp" ) , "should have c1.temp: {:?}" , names) ;
846- assert ! ( names. contains( & "c1.pressure" ) , "should have c1.pressure: {:?}" , names) ;
860+ assert ! (
861+ names. contains( & "c1.temp" ) ,
862+ "should have c1.temp: {:?}" ,
863+ names
864+ ) ;
865+ assert ! (
866+ names. contains( & "c1.pressure" ) ,
867+ "should have c1.pressure: {:?}" ,
868+ names
869+ ) ;
847870
848871 // Each expanded connection should be of kind Port (since the features are DataPort)
849872 for sc in & fg_semantic {
850- assert_eq ! ( sc. kind, ConnectionKind :: Port , "expanded FG connection should be Port kind" ) ;
873+ assert_eq ! (
874+ sc. kind,
875+ ConnectionKind :: Port ,
876+ "expanded FG connection should be Port kind"
877+ ) ;
851878 }
852879 }
853880
@@ -1057,13 +1084,15 @@ mod tests {
10571084 diags. iter( ) . map( |d| & d. message) . collect:: <Vec <_>>( )
10581085 ) ;
10591086
1060- let unmatched: Vec < _ > = diags. iter ( )
1087+ let unmatched: Vec < _ > = diags
1088+ . iter ( )
10611089 . filter ( |d| d. message . contains ( "no matching" ) )
10621090 . collect ( ) ;
10631091 assert_eq ! ( unmatched. len( ) , 1 , "pressure should be unmatched" ) ;
10641092 assert ! ( unmatched[ 0 ] . message. contains( "pressure" ) ) ;
10651093
1066- let mismatches: Vec < _ > = diags. iter ( )
1094+ let mismatches: Vec < _ > = diags
1095+ . iter ( )
10671096 . filter ( |d| d. message . contains ( "incompatible directions" ) )
10681097 . collect ( ) ;
10691098 assert_eq ! ( mismatches. len( ) , 1 , "temp should have direction mismatch" ) ;
@@ -1121,20 +1150,21 @@ mod tests {
11211150 // Expand both and verify they are complements
11221151 let scope = GlobalScope :: from_trees ( vec ! [ Arc :: new( tree) ] ) ;
11231152
1124- let src_expanded = expand_feature_group (
1125- & scope, & Name :: new ( "P" ) , & Name :: new ( "SensorOutput" ) , false ,
1126- ) ;
1127- let dst_expanded = expand_feature_group (
1128- & scope, & Name :: new ( "P" ) , & Name :: new ( "SensorInput" ) , false ,
1129- ) ;
1153+ let src_expanded =
1154+ expand_feature_group ( & scope, & Name :: new ( "P" ) , & Name :: new ( "SensorOutput" ) , false ) ;
1155+ let dst_expanded =
1156+ expand_feature_group ( & scope, & Name :: new ( "P" ) , & Name :: new ( "SensorInput" ) , false ) ;
11301157
11311158 assert_eq ! ( src_expanded. len( ) , 2 ) ;
11321159 assert_eq ! ( dst_expanded. len( ) , 2 ) ;
11331160
11341161 // SensorOutput: temp=Out, pressure=Out
11351162 // SensorInput (inverse): temp=In, pressure=In
11361163 let result = validate_complement ( & src_expanded, & dst_expanded) ;
1137- assert ! ( result. unmatched_source. is_empty( ) , "inverse should match all features" ) ;
1164+ assert ! (
1165+ result. unmatched_source. is_empty( ) ,
1166+ "inverse should match all features"
1167+ ) ;
11381168 assert ! (
11391169 result. direction_mismatches. is_empty( ) ,
11401170 "inverse should have complementary directions: {:?}" ,
0 commit comments