1111
1212use std:: collections:: HashMap ;
1313use std:: fmt;
14- use std:: fs:: { self , File } ;
15- use std:: io:: prelude:: * ;
14+ use std:: fs;
1615use std:: path:: Path ;
1716
18- use regex:: { Regex , escape } ;
17+ use regex:: Regex ;
1918
2019mod version;
2120use version:: Version ;
@@ -51,20 +50,48 @@ pub struct Feature {
5150
5251pub type Features = HashMap < String , Feature > ;
5352
54- pub fn check ( path : & Path , bad : & mut bool , quiet : bool ) {
53+ pub struct CollectedFeatures {
54+ pub lib : Features ,
55+ pub lang : Features ,
56+ }
57+
58+ // Currently only used for unstable book generation
59+ pub fn collect_lib_features ( base_src_path : & Path ) -> Features {
60+ let mut lib_features = Features :: new ( ) ;
61+
62+ // This library feature is defined in the `compiler_builtins` crate, which
63+ // has been moved out-of-tree. Now it can no longer be auto-discovered by
64+ // `tidy`, because we need to filter out its (submodule) directory. Manually
65+ // add it to the set of known library features so we can still generate docs.
66+ lib_features. insert ( "compiler_builtins_lib" . to_owned ( ) , Feature {
67+ level : Status :: Unstable ,
68+ since : None ,
69+ has_gate_test : false ,
70+ tracking_issue : None ,
71+ } ) ;
72+
73+ map_lib_features ( base_src_path,
74+ & mut |res, _, _| {
75+ if let Ok ( ( name, feature) ) = res {
76+ lib_features. insert ( name. to_owned ( ) , feature) ;
77+ }
78+ } ) ;
79+ lib_features
80+ }
81+
82+ pub fn check ( path : & Path , bad : & mut bool , verbose : bool ) -> CollectedFeatures {
5583 let mut features = collect_lang_features ( path, bad) ;
5684 assert ! ( !features. is_empty( ) ) ;
5785
5886 let lib_features = get_and_check_lib_features ( path, bad, & features) ;
5987 assert ! ( !lib_features. is_empty( ) ) ;
6088
61- let mut contents = String :: new ( ) ;
62-
6389 super :: walk_many ( & [ & path. join ( "test/ui" ) ,
6490 & path. join ( "test/ui-fulldeps" ) ,
6591 & path. join ( "test/compile-fail" ) ] ,
6692 & mut |path| super :: filter_dirs ( path) ,
67- & mut |file| {
93+ & mut |entry, contents| {
94+ let file = entry. path ( ) ;
6895 let filename = file. file_name ( ) . unwrap ( ) . to_string_lossy ( ) ;
6996 if !filename. ends_with ( ".rs" ) || filename == "features.rs" ||
7097 filename == "diagnostic_list.rs" {
@@ -74,9 +101,6 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
74101 let filen_underscore = filename. replace ( '-' , "_" ) . replace ( ".rs" , "" ) ;
75102 let filename_is_gate_test = test_filen_gate ( & filen_underscore, & mut features) ;
76103
77- contents. truncate ( 0 ) ;
78- t ! ( t!( File :: open( & file) , & file) . read_to_string( & mut contents) ) ;
79-
80104 for ( i, line) in contents. lines ( ) . enumerate ( ) {
81105 let mut err = |msg : & str | {
82106 tidy_error ! ( bad, "{}:{}: {}" , file. display( ) , i + 1 , msg) ;
@@ -130,21 +154,23 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
130154 }
131155
132156 if * bad {
133- return ;
134- }
135- if quiet {
136- println ! ( "* {} features" , features. len( ) ) ;
137- return ;
157+ return CollectedFeatures { lib : lib_features, lang : features } ;
138158 }
139159
140- let mut lines = Vec :: new ( ) ;
141- lines. extend ( format_features ( & features, "lang" ) ) ;
142- lines. extend ( format_features ( & lib_features, "lib" ) ) ;
160+ if verbose {
161+ let mut lines = Vec :: new ( ) ;
162+ lines. extend ( format_features ( & features, "lang" ) ) ;
163+ lines. extend ( format_features ( & lib_features, "lib" ) ) ;
143164
144- lines. sort ( ) ;
145- for line in lines {
146- println ! ( "* {}" , line) ;
165+ lines. sort ( ) ;
166+ for line in lines {
167+ println ! ( "* {}" , line) ;
168+ }
169+ } else {
170+ println ! ( "* {} features" , features. len( ) ) ;
147171 }
172+
173+ CollectedFeatures { lib : lib_features, lang : features }
148174}
149175
150176fn format_features < ' a > ( features : & ' a Features , family : & ' a str ) -> impl Iterator < Item = String > + ' a {
@@ -159,8 +185,19 @@ fn format_features<'a>(features: &'a Features, family: &'a str) -> impl Iterator
159185}
160186
161187fn find_attr_val < ' a > ( line : & ' a str , attr : & str ) -> Option < & ' a str > {
162- let r = Regex :: new ( & format ! ( r#"{}\s*=\s*"([^"]*)""# , escape( attr) ) )
163- . expect ( "malformed regex for find_attr_val" ) ;
188+ lazy_static:: lazy_static! {
189+ static ref ISSUE : Regex = Regex :: new( r#"issue\s*=\s*"([^"]*)""# ) . unwrap( ) ;
190+ static ref FEATURE : Regex = Regex :: new( r#"feature\s*=\s*"([^"]*)""# ) . unwrap( ) ;
191+ static ref SINCE : Regex = Regex :: new( r#"since\s*=\s*"([^"]*)""# ) . unwrap( ) ;
192+ }
193+
194+ let r = match attr {
195+ "issue" => & * ISSUE ,
196+ "feature" => & * FEATURE ,
197+ "since" => & * SINCE ,
198+ _ => unimplemented ! ( "{} not handled" , attr) ,
199+ } ;
200+
164201 r. captures ( line)
165202 . and_then ( |c| c. get ( 1 ) )
166203 . map ( |m| m. as_str ( ) )
@@ -175,9 +212,11 @@ fn test_find_attr_val() {
175212}
176213
177214fn test_filen_gate ( filen_underscore : & str , features : & mut Features ) -> bool {
178- if filen_underscore. starts_with ( "feature_gate" ) {
215+ let prefix = "feature_gate_" ;
216+ if filen_underscore. starts_with ( prefix) {
179217 for ( n, f) in features. iter_mut ( ) {
180- if filen_underscore == format ! ( "feature_gate_{}" , n) {
218+ // Equivalent to filen_underscore == format!("feature_gate_{}", n)
219+ if & filen_underscore[ prefix. len ( ) ..] == n {
181220 f. has_gate_test = true ;
182221 return true ;
183222 }
@@ -295,32 +334,6 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
295334 . collect ( )
296335}
297336
298- pub fn collect_lib_features ( base_src_path : & Path ) -> Features {
299- let mut lib_features = Features :: new ( ) ;
300-
301- // This library feature is defined in the `compiler_builtins` crate, which
302- // has been moved out-of-tree. Now it can no longer be auto-discovered by
303- // `tidy`, because we need to filter out its (submodule) directory. Manually
304- // add it to the set of known library features so we can still generate docs.
305- lib_features. insert ( "compiler_builtins_lib" . to_owned ( ) , Feature {
306- level : Status :: Unstable ,
307- since : None ,
308- has_gate_test : false ,
309- tracking_issue : None ,
310- } ) ;
311-
312- map_lib_features ( base_src_path,
313- & mut |res, _, _| {
314- if let Ok ( ( name, feature) ) = res {
315- if lib_features. contains_key ( name) {
316- return ;
317- }
318- lib_features. insert ( name. to_owned ( ) , feature) ;
319- }
320- } ) ;
321- lib_features
322- }
323-
324337fn get_and_check_lib_features ( base_src_path : & Path ,
325338 bad : & mut bool ,
326339 lang_features : & Features ) -> Features {
@@ -355,20 +368,25 @@ fn get_and_check_lib_features(base_src_path: &Path,
355368
356369fn map_lib_features ( base_src_path : & Path ,
357370 mf : & mut dyn FnMut ( Result < ( & str , Feature ) , & str > , & Path , usize ) ) {
358- let mut contents = String :: new ( ) ;
359371 super :: walk ( base_src_path,
360372 & mut |path| super :: filter_dirs ( path) || path. ends_with ( "src/test" ) ,
361- & mut |file| {
373+ & mut |entry, contents| {
374+ let file = entry. path ( ) ;
362375 let filename = file. file_name ( ) . unwrap ( ) . to_string_lossy ( ) ;
363376 if !filename. ends_with ( ".rs" ) || filename == "features.rs" ||
364377 filename == "diagnostic_list.rs" {
365378 return ;
366379 }
367380
368- contents. truncate ( 0 ) ;
369- t ! ( t!( File :: open( & file) , & file) . read_to_string( & mut contents) ) ;
381+ // This is an early exit -- all the attributes we're concerned with must contain this:
382+ // * rustc_const_unstable(
383+ // * unstable(
384+ // * stable(
385+ if !contents. contains ( "stable(" ) {
386+ return ;
387+ }
370388
371- let mut becoming_feature: Option < ( String , Feature ) > = None ;
389+ let mut becoming_feature: Option < ( & str , Feature ) > = None ;
372390 for ( i, line) in contents. lines ( ) . enumerate ( ) {
373391 macro_rules! err {
374392 ( $msg: expr) => { {
@@ -447,7 +465,7 @@ fn map_lib_features(base_src_path: &Path,
447465 if line. contains ( ']' ) {
448466 mf ( Ok ( ( feature_name, feature) ) , file, i + 1 ) ;
449467 } else {
450- becoming_feature = Some ( ( feature_name. to_owned ( ) , feature) ) ;
468+ becoming_feature = Some ( ( feature_name, feature) ) ;
451469 }
452470 }
453471 } ) ;
0 commit comments