1010//! * Language features in a group are sorted by feature name.
1111
1212use crate :: walk:: { filter_dirs, walk, walk_many} ;
13- use std:: collections:: HashMap ;
13+ use std:: collections:: hash_map :: { Entry , HashMap } ;
1414use std:: fmt;
1515use std:: fs;
1616use std:: num:: NonZeroU32 ;
@@ -280,13 +280,14 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
280280}
281281
282282pub fn collect_lang_features ( base_compiler_path : & Path , bad : & mut bool ) -> Features {
283- let mut all = collect_lang_features_in ( base_compiler_path, "active.rs" , bad) ;
284- all. extend ( collect_lang_features_in ( base_compiler_path, "accepted.rs" , bad) ) ;
285- all. extend ( collect_lang_features_in ( base_compiler_path, "removed.rs" , bad) ) ;
286- all
283+ let mut features = Features :: new ( ) ;
284+ collect_lang_features_in ( & mut features, base_compiler_path, "active.rs" , bad) ;
285+ collect_lang_features_in ( & mut features, base_compiler_path, "accepted.rs" , bad) ;
286+ collect_lang_features_in ( & mut features, base_compiler_path, "removed.rs" , bad) ;
287+ features
287288}
288289
289- fn collect_lang_features_in ( base : & Path , file : & str , bad : & mut bool ) -> Features {
290+ fn collect_lang_features_in ( features : & mut Features , base : & Path , file : & str , bad : & mut bool ) {
290291 let path = base. join ( "rustc_feature" ) . join ( "src" ) . join ( file) ;
291292 let contents = t ! ( fs:: read_to_string( & path) ) ;
292293
@@ -298,135 +299,145 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
298299 let mut in_feature_group = false ;
299300 let mut prev_names = vec ! [ ] ;
300301
301- contents
302- . lines ( )
303- . zip ( 1 ..)
304- . filter_map ( |( line, line_number) | {
305- let line = line. trim ( ) ;
306-
307- // Within -start and -end, the tracking issue can be omitted.
308- match line {
309- "// no-tracking-issue-start" => {
310- next_feature_omits_tracking_issue = true ;
311- return None ;
312- }
313- "// no-tracking-issue-end" => {
314- next_feature_omits_tracking_issue = false ;
315- return None ;
316- }
317- _ => { }
302+ let lines = contents. lines ( ) . zip ( 1 ..) ;
303+ for ( line, line_number) in lines {
304+ let line = line. trim ( ) ;
305+
306+ // Within -start and -end, the tracking issue can be omitted.
307+ match line {
308+ "// no-tracking-issue-start" => {
309+ next_feature_omits_tracking_issue = true ;
310+ continue ;
318311 }
312+ "// no-tracking-issue-end" => {
313+ next_feature_omits_tracking_issue = false ;
314+ continue ;
315+ }
316+ _ => { }
317+ }
319318
320- if line. starts_with ( FEATURE_GROUP_START_PREFIX ) {
321- if in_feature_group {
322- tidy_error ! (
323- bad,
324- "{}:{}: \
319+ if line. starts_with ( FEATURE_GROUP_START_PREFIX ) {
320+ if in_feature_group {
321+ tidy_error ! (
322+ bad,
323+ "{}:{}: \
325324 new feature group is started without ending the previous one",
326- path. display( ) ,
327- line_number,
328- ) ;
329- }
330-
331- in_feature_group = true ;
332- prev_names = vec ! [ ] ;
333- return None ;
334- } else if line. starts_with ( FEATURE_GROUP_END_PREFIX ) {
335- in_feature_group = false ;
336- prev_names = vec ! [ ] ;
337- return None ;
325+ path. display( ) ,
326+ line_number,
327+ ) ;
338328 }
339329
340- let mut parts = line. split ( ',' ) ;
341- let level = match parts. next ( ) . map ( |l| l. trim ( ) . trim_start_matches ( '(' ) ) {
342- Some ( "active" ) => Status :: Unstable ,
343- Some ( "incomplete" ) => Status :: Unstable ,
344- Some ( "removed" ) => Status :: Removed ,
345- Some ( "accepted" ) => Status :: Stable ,
346- _ => return None ,
347- } ;
348- let name = parts. next ( ) . unwrap ( ) . trim ( ) ;
349-
350- let since_str = parts. next ( ) . unwrap ( ) . trim ( ) . trim_matches ( '"' ) ;
351- let since = match since_str. parse ( ) {
352- Ok ( since) => Some ( since) ,
353- Err ( err) => {
354- tidy_error ! (
355- bad,
356- "{}:{}: failed to parse since: {} ({:?})" ,
357- path. display( ) ,
358- line_number,
359- since_str,
360- err,
361- ) ;
362- None
363- }
364- } ;
365- if in_feature_group {
366- if prev_names. last ( ) > Some ( & name) {
367- // This assumes the user adds the feature name at the end of the list, as we're
368- // not looking ahead.
369- let correct_index = match prev_names. binary_search ( & name) {
370- Ok ( _) => {
371- // This only occurs when the feature name has already been declared.
372- tidy_error ! (
373- bad,
374- "{}:{}: duplicate feature {}" ,
375- path. display( ) ,
376- line_number,
377- name,
378- ) ;
379- // skip any additional checks for this line
380- return None ;
381- }
382- Err ( index) => index,
383- } ;
330+ in_feature_group = true ;
331+ prev_names = vec ! [ ] ;
332+ continue ;
333+ } else if line. starts_with ( FEATURE_GROUP_END_PREFIX ) {
334+ in_feature_group = false ;
335+ prev_names = vec ! [ ] ;
336+ continue ;
337+ }
384338
385- let correct_placement = if correct_index == 0 {
386- "at the beginning of the feature group" . to_owned ( )
387- } else if correct_index == prev_names. len ( ) {
388- // I don't believe this is reachable given the above assumption, but it
389- // doesn't hurt to be safe.
390- "at the end of the feature group" . to_owned ( )
391- } else {
392- format ! (
393- "between {} and {}" ,
394- prev_names[ correct_index - 1 ] ,
395- prev_names[ correct_index] ,
396- )
397- } ;
339+ let mut parts = line. split ( ',' ) ;
340+ let level = match parts. next ( ) . map ( |l| l. trim ( ) . trim_start_matches ( '(' ) ) {
341+ Some ( "active" ) => Status :: Unstable ,
342+ Some ( "incomplete" ) => Status :: Unstable ,
343+ Some ( "removed" ) => Status :: Removed ,
344+ Some ( "accepted" ) => Status :: Stable ,
345+ _ => continue ,
346+ } ;
347+ let name = parts. next ( ) . unwrap ( ) . trim ( ) ;
348+
349+ let since_str = parts. next ( ) . unwrap ( ) . trim ( ) . trim_matches ( '"' ) ;
350+ let since = match since_str. parse ( ) {
351+ Ok ( since) => Some ( since) ,
352+ Err ( err) => {
353+ tidy_error ! (
354+ bad,
355+ "{}:{}: failed to parse since: {} ({:?})" ,
356+ path. display( ) ,
357+ line_number,
358+ since_str,
359+ err,
360+ ) ;
361+ None
362+ }
363+ } ;
364+ if in_feature_group {
365+ if prev_names. last ( ) > Some ( & name) {
366+ // This assumes the user adds the feature name at the end of the list, as we're
367+ // not looking ahead.
368+ let correct_index = match prev_names. binary_search ( & name) {
369+ Ok ( _) => {
370+ // This only occurs when the feature name has already been declared.
371+ tidy_error ! (
372+ bad,
373+ "{}:{}: duplicate feature {}" ,
374+ path. display( ) ,
375+ line_number,
376+ name,
377+ ) ;
378+ // skip any additional checks for this line
379+ continue ;
380+ }
381+ Err ( index) => index,
382+ } ;
398383
399- tidy_error ! (
400- bad,
401- "{}:{}: feature {} is not sorted by feature name (should be {})" ,
402- path. display( ) ,
403- line_number,
404- name,
405- correct_placement,
406- ) ;
407- }
408- prev_names. push ( name) ;
384+ let correct_placement = if correct_index == 0 {
385+ "at the beginning of the feature group" . to_owned ( )
386+ } else if correct_index == prev_names. len ( ) {
387+ // I don't believe this is reachable given the above assumption, but it
388+ // doesn't hurt to be safe.
389+ "at the end of the feature group" . to_owned ( )
390+ } else {
391+ format ! (
392+ "between {} and {}" ,
393+ prev_names[ correct_index - 1 ] ,
394+ prev_names[ correct_index] ,
395+ )
396+ } ;
397+
398+ tidy_error ! (
399+ bad,
400+ "{}:{}: feature {} is not sorted by feature name (should be {})" ,
401+ path. display( ) ,
402+ line_number,
403+ name,
404+ correct_placement,
405+ ) ;
409406 }
407+ prev_names. push ( name) ;
408+ }
410409
411- let issue_str = parts. next ( ) . unwrap ( ) . trim ( ) ;
412- let tracking_issue = if issue_str. starts_with ( "None" ) {
413- if level == Status :: Unstable && !next_feature_omits_tracking_issue {
414- tidy_error ! (
415- bad,
416- "{}:{}: no tracking issue for feature {}" ,
417- path. display( ) ,
418- line_number,
419- name,
420- ) ;
421- }
422- None
423- } else {
424- let s = issue_str. split ( '(' ) . nth ( 1 ) . unwrap ( ) . split ( ')' ) . next ( ) . unwrap ( ) ;
425- Some ( s. parse ( ) . unwrap ( ) )
426- } ;
427- Some ( ( name. to_owned ( ) , Feature { level, since, has_gate_test : false , tracking_issue } ) )
428- } )
429- . collect ( )
410+ let issue_str = parts. next ( ) . unwrap ( ) . trim ( ) ;
411+ let tracking_issue = if issue_str. starts_with ( "None" ) {
412+ if level == Status :: Unstable && !next_feature_omits_tracking_issue {
413+ tidy_error ! (
414+ bad,
415+ "{}:{}: no tracking issue for feature {}" ,
416+ path. display( ) ,
417+ line_number,
418+ name,
419+ ) ;
420+ }
421+ None
422+ } else {
423+ let s = issue_str. split ( '(' ) . nth ( 1 ) . unwrap ( ) . split ( ')' ) . next ( ) . unwrap ( ) ;
424+ Some ( s. parse ( ) . unwrap ( ) )
425+ } ;
426+ match features. entry ( name. to_owned ( ) ) {
427+ Entry :: Occupied ( e) => {
428+ tidy_error ! (
429+ bad,
430+ "{}:{} feature {name} already specified with status '{}'" ,
431+ path. display( ) ,
432+ line_number,
433+ e. get( ) . level,
434+ ) ;
435+ }
436+ Entry :: Vacant ( e) => {
437+ e. insert ( Feature { level, since, has_gate_test : false , tracking_issue } ) ;
438+ }
439+ }
440+ }
430441}
431442
432443fn get_and_check_lib_features (
0 commit comments