@@ -1363,12 +1363,12 @@ impl<'gctx> Workspace<'gctx> {
13631363 }
13641364 }
13651365
1366- fn report_unknown_features_error (
1366+ fn missing_feature_spelling_suggestions (
13671367 & self ,
1368- specs : & [ PackageIdSpec ] ,
1368+ selected_members : & [ & Package ] ,
13691369 cli_features : & CliFeatures ,
13701370 found_features : & BTreeSet < FeatureValue > ,
1371- ) -> CargoResult < ( ) > {
1371+ ) -> Vec < String > {
13721372 // Keeps track of which features were contained in summary of `member` to suggest similar features in errors
13731373 let mut summary_features: Vec < InternedString > = Default :: default ( ) ;
13741374
@@ -1387,10 +1387,7 @@ impl<'gctx> Workspace<'gctx> {
13871387 let mut optional_dependency_names_per_member: BTreeMap < & Package , BTreeSet < InternedString > > =
13881388 Default :: default ( ) ;
13891389
1390- for member in self
1391- . members ( )
1392- . filter ( |m| specs. iter ( ) . any ( |spec| spec. matches ( m. package_id ( ) ) ) )
1393- {
1390+ for & member in selected_members {
13941391 // Only include features this member defines.
13951392 let summary = member. summary ( ) ;
13961393
@@ -1428,7 +1425,7 @@ impl<'gctx> Workspace<'gctx> {
14281425 edit_distance ( a. as_str ( ) , b. as_str ( ) , 3 ) . is_some ( )
14291426 } ;
14301427
1431- let suggestions : Vec < _ > = cli_features
1428+ cli_features
14321429 . features
14331430 . difference ( found_features)
14341431 . map ( |feature| match feature {
@@ -1522,27 +1519,86 @@ impl<'gctx> Workspace<'gctx> {
15221519 } )
15231520 . sorted ( )
15241521 . take ( 5 )
1525- . collect ( ) ;
1522+ . collect ( )
1523+ }
15261524
1525+ fn report_unknown_features_error (
1526+ & self ,
1527+ specs : & [ PackageIdSpec ] ,
1528+ cli_features : & CliFeatures ,
1529+ found_features : & BTreeSet < FeatureValue > ,
1530+ ) -> CargoResult < ( ) > {
15271531 let unknown: Vec < _ > = cli_features
15281532 . features
15291533 . difference ( found_features)
15301534 . map ( |feature| feature. to_string ( ) )
15311535 . sorted ( )
15321536 . collect ( ) ;
15331537
1534- if suggestions. is_empty ( ) {
1535- bail ! (
1536- "none of the selected packages contains these features: {}" ,
1538+ let ( selected_members, unselected_members) : ( Vec < _ > , Vec < _ > ) = self
1539+ . members ( )
1540+ . partition ( |member| specs. iter ( ) . any ( |spec| spec. matches ( member. package_id ( ) ) ) ) ;
1541+
1542+ let missing_packages_with_the_features = unselected_members
1543+ . into_iter ( )
1544+ . filter ( |member| {
1545+ unknown
1546+ . iter ( )
1547+ . any ( |feature| member. summary ( ) . features ( ) . contains_key ( & * * feature) )
1548+ } )
1549+ . map ( |m| m. name ( ) )
1550+ . collect_vec ( ) ;
1551+
1552+ let these_features = if unknown. len ( ) == 1 {
1553+ "this feature"
1554+ } else {
1555+ "these features"
1556+ } ;
1557+ let mut msg = if let [ singular] = & selected_members[ ..] {
1558+ format ! (
1559+ "the package '{}' does not contain {these_features}: {}" ,
1560+ singular. name( ) ,
15371561 unknown. join( ", " )
1538- ) ;
1562+ )
15391563 } else {
1540- bail ! (
1541- "none of the selected packages contains these features: {}, did you mean: {}?" ,
1542- unknown. join( ", " ) ,
1543- suggestions. join( ", " )
1564+ let names = selected_members. iter ( ) . map ( |m| m. name ( ) ) . join ( ", " ) ;
1565+ format ! ( "none of the selected packages contains {these_features}: {}\n selected packages: {names}" , unknown. join( ", " ) )
1566+ } ;
1567+
1568+ use std:: fmt:: Write ;
1569+ if !missing_packages_with_the_features. is_empty ( ) {
1570+ write ! (
1571+ & mut msg,
1572+ "\n help: package{} with the missing feature{}: {}" ,
1573+ if missing_packages_with_the_features. len( ) != 1 {
1574+ "s"
1575+ } else {
1576+ ""
1577+ } ,
1578+ if unknown. len( ) != 1 { "s" } else { "" } ,
1579+ missing_packages_with_the_features. join( ", " )
1580+ ) ?;
1581+ } else {
1582+ let suggestions = self . missing_feature_spelling_suggestions (
1583+ & selected_members,
1584+ cli_features,
1585+ found_features,
15441586 ) ;
1587+ if !suggestions. is_empty ( ) {
1588+ write ! (
1589+ & mut msg,
1590+ "\n help: there {}: {}" ,
1591+ if suggestions. len( ) == 1 {
1592+ "is a similarly named feature"
1593+ } else {
1594+ "are similarly named features"
1595+ } ,
1596+ suggestions. join( ", " )
1597+ ) ?;
1598+ }
15451599 }
1600+
1601+ bail ! ( "{msg}" )
15461602 }
15471603
15481604 /// New command-line feature selection behavior with resolver = "2" or the
0 commit comments