@@ -3,14 +3,17 @@ use crate::types::{
33    DisallowedPath ,  DisallowedPathWithoutReplacement ,  MacroMatcher ,  MatchLintBehaviour ,  PubUnderscoreFieldsBehaviour , 
44    Rename ,  SourceItemOrdering ,  SourceItemOrderingCategory ,  SourceItemOrderingModuleItemGroupings , 
55    SourceItemOrderingModuleItemKind ,  SourceItemOrderingTraitAssocItemKind ,  SourceItemOrderingTraitAssocItemKinds , 
6+     SourceItemOrderingWithinModuleItemGroupings , 
67} ; 
78use  clippy_utils:: msrvs:: Msrv ; 
9+ use  itertools:: Itertools ; 
810use  rustc_errors:: Applicability ; 
911use  rustc_session:: Session ; 
1012use  rustc_span:: edit_distance:: edit_distance; 
1113use  rustc_span:: { BytePos ,  Pos ,  SourceFile ,  Span ,  SyntaxContext } ; 
1214use  serde:: de:: { IgnoredAny ,  IntoDeserializer ,  MapAccess ,  Visitor } ; 
1315use  serde:: { Deserialize ,  Deserializer ,  Serialize } ; 
16+ use  std:: collections:: HashMap ; 
1417use  std:: fmt:: { Debug ,  Display ,  Formatter } ; 
1518use  std:: ops:: Range ; 
1619use  std:: path:: PathBuf ; 
@@ -79,6 +82,7 @@ const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = {
7982#[ derive( Default ) ]  
8083struct  TryConf  { 
8184    conf :  Conf , 
85+     value_spans :  HashMap < String ,  Range < usize > > , 
8286    errors :  Vec < ConfError > , 
8387    warnings :  Vec < ConfError > , 
8488} 
@@ -87,6 +91,7 @@ impl TryConf {
8791    fn  from_toml_error ( file :  & SourceFile ,  error :  & toml:: de:: Error )  -> Self  { 
8892        Self  { 
8993            conf :  Conf :: default ( ) , 
94+             value_spans :  HashMap :: default ( ) , 
9095            errors :  vec ! [ ConfError :: from_toml( file,  error) ] , 
9196            warnings :  vec ! [ ] , 
9297        } 
@@ -210,6 +215,7 @@ macro_rules! define_Conf {
210215            } 
211216
212217            fn  visit_map<V >( self ,  mut  map:  V )  -> Result <Self :: Value ,  V :: Error > where  V :  MapAccess <' de> { 
218+                 let  mut  value_spans = HashMap :: new( ) ; 
213219                let  mut  errors = Vec :: new( ) ; 
214220                let  mut  warnings = Vec :: new( ) ; 
215221                $( let  mut  $name = None ; ) * 
@@ -232,6 +238,7 @@ macro_rules! define_Conf {
232238                                    } 
233239                                    None  => { 
234240                                        $name = Some ( value) ; 
241+                                         value_spans. insert( name. get_ref( ) . as_str( ) . to_string( ) ,  value_span) ; 
235242                                        // $new_conf is the same as one of the defined `$name`s, so 
236243                                        // this variable is defined in line 2 of this function. 
237244                                        $( match  $new_conf { 
@@ -250,7 +257,7 @@ macro_rules! define_Conf {
250257                    } 
251258                } 
252259                let  conf = Conf  {  $( $name:  $name. unwrap_or_else( defaults:: $name) , ) *  } ; 
253-                 Ok ( TryConf  {  conf,  errors,  warnings } ) 
260+                 Ok ( TryConf  {  conf,  value_spans ,   errors,  warnings } ) 
254261            } 
255262        } 
256263
@@ -596,6 +603,13 @@ define_Conf! {
596603    /// The named groupings of different source item kinds within modules. 
597604[ lints( arbitrary_source_item_ordering) ] 
598605    module_item_order_groupings:  SourceItemOrderingModuleItemGroupings  = DEFAULT_MODULE_ITEM_ORDERING_GROUPS . into( ) , 
606+     /// Whether the items within module groups should be ordered alphabetically or not. 
607+ /// 
608+ /// This option can be configured to "all", "none", or a list of specific grouping names that should be checked 
609+ /// (e.g. only "enums"). 
610+ [ lints( arbitrary_source_item_ordering) ] 
611+     module_items_ordered_within_groupings:  SourceItemOrderingWithinModuleItemGroupings  =
612+         SourceItemOrderingWithinModuleItemGroupings :: None , 
599613    /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` 
600614[ default_text = "current version" ] 
601615    #[ lints( 
@@ -815,6 +829,36 @@ fn deserialize(file: &SourceFile) -> TryConf {
815829                & mut  conf. conf . allow_renamed_params_for , 
816830                DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS , 
817831            ) ; 
832+ 
833+             // Confirms that the user has not accidentally configured ordering requirements for groups that 
834+             // aren't configured. 
835+             if  let  SourceItemOrderingWithinModuleItemGroupings :: Custom ( groupings)  =
836+                 & conf. conf . module_items_ordered_within_groupings 
837+             { 
838+                 for  grouping in  groupings { 
839+                     if  !conf. conf . module_item_order_groupings . is_grouping ( grouping)  { 
840+                         // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful 
841+                         // info for the user instead. 
842+ 
843+                         let  names = conf. conf . module_item_order_groupings . grouping_names ( ) ; 
844+                         let  suggestion = suggest_candidate ( grouping,  names. iter ( ) . map ( String :: as_str) ) 
845+                             . map ( |s| format ! ( " perhaps you meant `{s}`?" ) ) 
846+                             . unwrap_or_default ( ) ; 
847+                         let  names = names. iter ( ) . map ( |s| format ! ( "`{s}`" ) ) . join ( ", " ) ; 
848+                         let  message = format ! ( 
849+                             "unknown ordering group: `{grouping}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}" 
850+                         ) ; 
851+ 
852+                         let  span = conf
853+                             . value_spans 
854+                             . get ( "module_item_order_groupings" ) 
855+                             . cloned ( ) 
856+                             . unwrap_or_default ( ) ; 
857+                         conf. errors . push ( ConfError :: spanned ( file,  message,  None ,  span) ) ; 
858+                     } 
859+                 } 
860+             } 
861+ 
818862            // TODO: THIS SHOULD BE TESTED, this comment will be gone soon 
819863            if  conf. conf . allowed_idents_below_min_chars . iter ( ) . any ( |e| e == ".." )  { 
820864                conf. conf 
@@ -860,6 +904,7 @@ impl Conf {
860904
861905        let  TryConf  { 
862906            mut  conf, 
907+             value_spans :  _, 
863908            errors, 
864909            warnings, 
865910        }  = match  path { 
@@ -950,17 +995,10 @@ impl serde::de::Error for FieldError {
950995            } 
951996        } 
952997
953-         let  suggestion = expected
954-             . iter ( ) 
955-             . filter_map ( |expected| { 
956-                 let  dist = edit_distance ( field,  expected,  4 ) ?; 
957-                 Some ( ( dist,  expected) ) 
958-             } ) 
959-             . min_by_key ( |& ( dist,  _) | dist) 
960-             . map ( |( _,  suggestion) | Suggestion  { 
961-                 message :  "perhaps you meant" , 
962-                 suggestion, 
963-             } ) ; 
998+         let  suggestion = suggest_candidate ( field,  expected) . map ( |suggestion| Suggestion  { 
999+             message :  "perhaps you meant" , 
1000+             suggestion, 
1001+         } ) ; 
9641002
9651003        Self  {  error :  msg,  suggestion } 
9661004    } 
@@ -998,6 +1036,22 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
9981036    ( rows,  column_widths) 
9991037} 
10001038
1039+ /// Given a user-provided value that couldn't be matched to a known option, finds the most likely 
1040+ /// candidate among candidates that the user might have meant. 
1041+ fn  suggest_candidate < ' a ,  I > ( value :  & str ,  candidates :  I )  -> Option < & ' a  str > 
1042+ where 
1043+     I :  IntoIterator < Item  = & ' a  str > , 
1044+ { 
1045+     candidates
1046+         . into_iter ( ) 
1047+         . filter_map ( |expected| { 
1048+             let  dist = edit_distance ( value,  expected,  4 ) ?; 
1049+             Some ( ( dist,  expected) ) 
1050+         } ) 
1051+         . min_by_key ( |& ( dist,  _) | dist) 
1052+         . map ( |( _,  suggestion) | suggestion) 
1053+ } 
1054+ 
10011055#[ cfg( test) ]  
10021056mod  tests { 
10031057    use  serde:: de:: IgnoredAny ; 
0 commit comments