@@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
88use rustc_data_structures:: sync:: Lrc ;
99use rustc_errors:: emitter:: stderr_destination;
1010use rustc_errors:: { ColorConfig , ErrorGuaranteed , FatalError } ;
11- use rustc_hir:: def_id:: { CRATE_DEF_ID , LOCAL_CRATE } ;
11+ use rustc_hir:: def_id:: LOCAL_CRATE ;
1212use rustc_hir:: CRATE_HIR_ID ;
1313use rustc_interface:: interface;
1414use rustc_parse:: maybe_new_parser_from_source_str;
@@ -19,10 +19,9 @@ use rustc_session::parse::ParseSess;
1919use rustc_span:: edition:: Edition ;
2020use rustc_span:: source_map:: SourceMap ;
2121use rustc_span:: symbol:: sym;
22- use rustc_span:: { BytePos , FileName , Pos , Span , DUMMY_SP } ;
22+ use rustc_span:: FileName ;
2323use rustc_target:: spec:: { Target , TargetTriple } ;
2424
25- use std:: env;
2625use std:: fs:: File ;
2726use std:: io:: { self , Write } ;
2827use std:: panic;
@@ -38,7 +37,8 @@ use crate::config::Options as RustdocOptions;
3837use crate :: html:: markdown:: { ErrorCodes , Ignore , LangString } ;
3938use crate :: lint:: init_lints;
4039
41- use self :: rust:: HirCollector ;
40+ use self :: markdown:: MdDoctest ;
41+ use self :: rust:: { HirCollector , RustDoctest } ;
4242
4343/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
4444#[ derive( Clone , Default ) ]
@@ -182,29 +182,19 @@ pub(crate) fn run(
182182 let mut collector = Collector :: new (
183183 tcx. crate_name ( LOCAL_CRATE ) . to_string ( ) ,
184184 options,
185- false ,
186185 opts,
187- Some ( compiler. sess . psess . clone_source_map ( ) ) ,
188- None ,
189- enable_per_target_ignores,
190186 file_path,
191187 ) ;
192188
193- let mut hir_collector = HirCollector {
194- sess : & compiler. sess ,
195- collector : & mut collector,
196- map : tcx. hir ( ) ,
197- codes : ErrorCodes :: from (
198- compiler. sess . opts . unstable_features . is_nightly_build ( ) ,
199- ) ,
189+ let hir_collector = HirCollector :: new (
190+ & compiler. sess ,
191+ tcx. hir ( ) ,
192+ ErrorCodes :: from ( compiler. sess . opts . unstable_features . is_nightly_build ( ) ) ,
193+ enable_per_target_ignores,
200194 tcx,
201- } ;
202- hir_collector. visit_testable (
203- "" . to_string ( ) ,
204- CRATE_DEF_ID ,
205- tcx. hir ( ) . span ( CRATE_HIR_ID ) ,
206- |this| tcx. hir ( ) . walk_toplevel_module ( this) ,
207195 ) ;
196+ let tests = hir_collector. collect_crate ( ) ;
197+ tests. into_iter ( ) . for_each ( |t| collector. add_test ( ScrapedDoctest :: Rust ( t) ) ) ;
208198
209199 collector
210200 } ) ;
@@ -986,6 +976,12 @@ impl IndividualTestOptions {
986976 }
987977}
988978
979+ /// A doctest scraped from the code, ready to be turned into a runnable test.
980+ enum ScrapedDoctest {
981+ Rust ( RustDoctest ) ,
982+ Markdown ( MdDoctest ) ,
983+ }
984+
989985pub ( crate ) trait DoctestVisitor {
990986 fn visit_test ( & mut self , test : String , config : LangString , line : usize ) ;
991987 fn get_line ( & self ) -> usize {
@@ -997,36 +993,9 @@ pub(crate) trait DoctestVisitor {
997993pub ( crate ) struct Collector {
998994 pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
999995
1000- // The name of the test displayed to the user, separated by `::`.
1001- //
1002- // In tests from Rust source, this is the path to the item
1003- // e.g., `["std", "vec", "Vec", "push"]`.
1004- //
1005- // In tests from a markdown file, this is the titles of all headers (h1~h6)
1006- // of the sections that contain the code block, e.g., if the markdown file is
1007- // written as:
1008- //
1009- // ``````markdown
1010- // # Title
1011- //
1012- // ## Subtitle
1013- //
1014- // ```rust
1015- // assert!(true);
1016- // ```
1017- // ``````
1018- //
1019- // the `names` vector of that test will be `["Title", "Subtitle"]`.
1020- names : Vec < String > ,
1021-
1022996 rustdoc_options : RustdocOptions ,
1023- use_headers : bool ,
1024- enable_per_target_ignores : bool ,
1025997 crate_name : String ,
1026998 opts : GlobalTestOptions ,
1027- position : Span ,
1028- source_map : Option < Lrc < SourceMap > > ,
1029- filename : Option < PathBuf > ,
1030999 visited_tests : FxHashMap < ( String , usize ) , usize > ,
10311000 unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
10321001 compiling_test_count : AtomicUsize ,
@@ -1037,74 +1006,48 @@ impl Collector {
10371006 pub ( crate ) fn new (
10381007 crate_name : String ,
10391008 rustdoc_options : RustdocOptions ,
1040- use_headers : bool ,
10411009 opts : GlobalTestOptions ,
1042- source_map : Option < Lrc < SourceMap > > ,
1043- filename : Option < PathBuf > ,
1044- enable_per_target_ignores : bool ,
10451010 arg_file : PathBuf ,
10461011 ) -> Collector {
10471012 Collector {
10481013 tests : Vec :: new ( ) ,
1049- names : Vec :: new ( ) ,
10501014 rustdoc_options,
1051- use_headers,
1052- enable_per_target_ignores,
10531015 crate_name,
10541016 opts,
1055- position : DUMMY_SP ,
1056- source_map,
1057- filename,
10581017 visited_tests : FxHashMap :: default ( ) ,
10591018 unused_extern_reports : Default :: default ( ) ,
10601019 compiling_test_count : AtomicUsize :: new ( 0 ) ,
10611020 arg_file,
10621021 }
10631022 }
10641023
1065- fn generate_name ( & self , line : usize , filename : & FileName ) -> String {
1066- let mut item_path = self . names . join ( "::" ) ;
1024+ fn generate_name ( & self , filename : & FileName , line : usize , logical_path : & [ String ] ) -> String {
1025+ let mut item_path = logical_path . join ( "::" ) ;
10671026 item_path. retain ( |c| c != ' ' ) ;
10681027 if !item_path. is_empty ( ) {
10691028 item_path. push ( ' ' ) ;
10701029 }
10711030 format ! ( "{} - {item_path}(line {line})" , filename. prefer_local( ) )
10721031 }
10731032
1074- pub ( crate ) fn set_position ( & mut self , position : Span ) {
1075- self . position = position;
1076- }
1077-
1078- fn get_filename ( & self ) -> FileName {
1079- if let Some ( ref source_map) = self . source_map {
1080- let filename = source_map. span_to_filename ( self . position ) ;
1081- if let FileName :: Real ( ref filename) = filename
1082- && let Ok ( cur_dir) = env:: current_dir ( )
1083- && let Some ( local_path) = filename. local_path ( )
1084- && let Ok ( path) = local_path. strip_prefix ( & cur_dir)
1085- {
1086- return path. to_owned ( ) . into ( ) ;
1033+ fn add_test ( & mut self , test : ScrapedDoctest ) {
1034+ let ( filename, line, logical_path, langstr, text) = match test {
1035+ ScrapedDoctest :: Rust ( RustDoctest { filename, line, logical_path, langstr, text } ) => {
1036+ ( filename, line, logical_path, langstr, text)
10871037 }
1088- filename
1089- } else if let Some ( ref filename) = self . filename {
1090- filename. clone ( ) . into ( )
1091- } else {
1092- FileName :: Custom ( "input" . to_owned ( ) )
1093- }
1094- }
1095- }
1038+ ScrapedDoctest :: Markdown ( MdDoctest { filename, line, logical_path, langstr, text } ) => {
1039+ ( filename, line, logical_path, langstr, text)
1040+ }
1041+ } ;
10961042
1097- impl DoctestVisitor for Collector {
1098- fn visit_test ( & mut self , test : String , config : LangString , line : usize ) {
1099- let filename = self . get_filename ( ) ;
1100- let name = self . generate_name ( line, & filename) ;
1043+ let name = self . generate_name ( & filename, line, & logical_path) ;
11011044 let crate_name = self . crate_name . clone ( ) ;
11021045 let opts = self . opts . clone ( ) ;
1103- let edition = config . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1046+ let edition = langstr . edition . unwrap_or ( self . rustdoc_options . edition ) ;
11041047 let target_str = self . rustdoc_options . target . to_string ( ) ;
11051048 let unused_externs = self . unused_extern_reports . clone ( ) ;
1106- let no_run = config . no_run || self . rustdoc_options . no_run ;
1107- if !config . compile_fail {
1049+ let no_run = langstr . no_run || self . rustdoc_options . no_run ;
1050+ if !langstr . compile_fail {
11081051 self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
11091052 }
11101053
@@ -1141,11 +1084,11 @@ impl DoctestVisitor for Collector {
11411084 let rustdoc_test_options =
11421085 IndividualTestOptions :: new ( & self . rustdoc_options , & self . arg_file , test_id) ;
11431086
1144- debug ! ( "creating test {name}: {test }" ) ;
1087+ debug ! ( "creating test {name}: {text }" ) ;
11451088 self . tests . push ( test:: TestDescAndFn {
11461089 desc : test:: TestDesc {
11471090 name : test:: DynTestName ( name) ,
1148- ignore : match config . ignore {
1091+ ignore : match langstr . ignore {
11491092 Ignore :: All => true ,
11501093 Ignore :: None => false ,
11511094 Ignore :: Some ( ref ignores) => ignores. iter ( ) . any ( |s| target_str. contains ( s) ) ,
@@ -1158,7 +1101,7 @@ impl DoctestVisitor for Collector {
11581101 end_col : 0 ,
11591102 // compiler failures are test failures
11601103 should_panic : test:: ShouldPanic :: No ,
1161- compile_fail : config . compile_fail ,
1104+ compile_fail : langstr . compile_fail ,
11621105 no_run,
11631106 test_type : test:: TestType :: DocTest ,
11641107 } ,
@@ -1167,11 +1110,11 @@ impl DoctestVisitor for Collector {
11671110 unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
11681111 } ;
11691112 let res = run_test (
1170- & test ,
1113+ & text ,
11711114 & crate_name,
11721115 line,
11731116 rustdoc_test_options,
1174- config ,
1117+ langstr ,
11751118 no_run,
11761119 & opts,
11771120 edition,
@@ -1234,59 +1177,6 @@ impl DoctestVisitor for Collector {
12341177 } ) ) ,
12351178 } ) ;
12361179 }
1237-
1238- fn get_line ( & self ) -> usize {
1239- if let Some ( ref source_map) = self . source_map {
1240- let line = self . position . lo ( ) . to_usize ( ) ;
1241- let line = source_map. lookup_char_pos ( BytePos ( line as u32 ) ) . line ;
1242- if line > 0 { line - 1 } else { line }
1243- } else {
1244- 0
1245- }
1246- }
1247-
1248- fn visit_header ( & mut self , name : & str , level : u32 ) {
1249- if self . use_headers {
1250- // We use these headings as test names, so it's good if
1251- // they're valid identifiers.
1252- let name = name
1253- . chars ( )
1254- . enumerate ( )
1255- . map ( |( i, c) | {
1256- if ( i == 0 && rustc_lexer:: is_id_start ( c) )
1257- || ( i != 0 && rustc_lexer:: is_id_continue ( c) )
1258- {
1259- c
1260- } else {
1261- '_'
1262- }
1263- } )
1264- . collect :: < String > ( ) ;
1265-
1266- // Here we try to efficiently assemble the header titles into the
1267- // test name in the form of `h1::h2::h3::h4::h5::h6`.
1268- //
1269- // Suppose that originally `self.names` contains `[h1, h2, h3]`...
1270- let level = level as usize ;
1271- if level <= self . names . len ( ) {
1272- // ... Consider `level == 2`. All headers in the lower levels
1273- // are irrelevant in this new level. So we should reset
1274- // `self.names` to contain headers until <h2>, and replace that
1275- // slot with the new name: `[h1, name]`.
1276- self . names . truncate ( level) ;
1277- self . names [ level - 1 ] = name;
1278- } else {
1279- // ... On the other hand, consider `level == 5`. This means we
1280- // need to extend `self.names` to contain five headers. We fill
1281- // in the missing level (<h4>) with `_`. Thus `self.names` will
1282- // become `[h1, h2, h3, "_", name]`.
1283- if level - 1 > self . names . len ( ) {
1284- self . names . resize ( level - 1 , "_" . to_owned ( ) ) ;
1285- }
1286- self . names . push ( name) ;
1287- }
1288- }
1289- }
12901180}
12911181
12921182#[ cfg( test) ] // used in tests
0 commit comments