@@ -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:: 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 } ) ;
@@ -985,6 +975,12 @@ impl IndividualTestOptions {
985975 }
986976}
987977
978+ /// A doctest scraped from the code, ready to be turned into a runnable test.
979+ enum ScrapedDoctest {
980+ Rust ( RustDoctest ) ,
981+ Markdown ( MdDoctest ) ,
982+ }
983+
988984pub ( crate ) trait DoctestVisitor {
989985 fn visit_test ( & mut self , test : String , config : LangString , line : usize ) ;
990986 fn get_line ( & self ) -> usize {
@@ -996,36 +992,9 @@ pub(crate) trait DoctestVisitor {
996992pub ( crate ) struct Collector {
997993 pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
998994
999- // The name of the test displayed to the user, separated by `::`.
1000- //
1001- // In tests from Rust source, this is the path to the item
1002- // e.g., `["std", "vec", "Vec", "push"]`.
1003- //
1004- // In tests from a markdown file, this is the titles of all headers (h1~h6)
1005- // of the sections that contain the code block, e.g., if the markdown file is
1006- // written as:
1007- //
1008- // ``````markdown
1009- // # Title
1010- //
1011- // ## Subtitle
1012- //
1013- // ```rust
1014- // assert!(true);
1015- // ```
1016- // ``````
1017- //
1018- // the `names` vector of that test will be `["Title", "Subtitle"]`.
1019- names : Vec < String > ,
1020-
1021995 rustdoc_options : RustdocOptions ,
1022- use_headers : bool ,
1023- enable_per_target_ignores : bool ,
1024996 crate_name : String ,
1025997 opts : GlobalTestOptions ,
1026- position : Span ,
1027- source_map : Option < Lrc < SourceMap > > ,
1028- filename : Option < PathBuf > ,
1029998 visited_tests : FxHashMap < ( String , usize ) , usize > ,
1030999 unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
10311000 compiling_test_count : AtomicUsize ,
@@ -1036,74 +1005,48 @@ impl Collector {
10361005 pub ( crate ) fn new (
10371006 crate_name : String ,
10381007 rustdoc_options : RustdocOptions ,
1039- use_headers : bool ,
10401008 opts : GlobalTestOptions ,
1041- source_map : Option < Lrc < SourceMap > > ,
1042- filename : Option < PathBuf > ,
1043- enable_per_target_ignores : bool ,
10441009 arg_file : PathBuf ,
10451010 ) -> Collector {
10461011 Collector {
10471012 tests : Vec :: new ( ) ,
1048- names : Vec :: new ( ) ,
10491013 rustdoc_options,
1050- use_headers,
1051- enable_per_target_ignores,
10521014 crate_name,
10531015 opts,
1054- position : DUMMY_SP ,
1055- source_map,
1056- filename,
10571016 visited_tests : FxHashMap :: default ( ) ,
10581017 unused_extern_reports : Default :: default ( ) ,
10591018 compiling_test_count : AtomicUsize :: new ( 0 ) ,
10601019 arg_file,
10611020 }
10621021 }
10631022
1064- fn generate_name ( & self , line : usize , filename : & FileName ) -> String {
1065- let mut item_path = self . names . join ( "::" ) ;
1023+ fn generate_name ( & self , filename : & FileName , line : usize , logical_path : & [ String ] ) -> String {
1024+ let mut item_path = logical_path . join ( "::" ) ;
10661025 item_path. retain ( |c| c != ' ' ) ;
10671026 if !item_path. is_empty ( ) {
10681027 item_path. push ( ' ' ) ;
10691028 }
10701029 format ! ( "{} - {item_path}(line {line})" , filename. prefer_local( ) )
10711030 }
10721031
1073- pub ( crate ) fn set_position ( & mut self , position : Span ) {
1074- self . position = position;
1075- }
1076-
1077- fn get_filename ( & self ) -> FileName {
1078- if let Some ( ref source_map) = self . source_map {
1079- let filename = source_map. span_to_filename ( self . position ) ;
1080- if let FileName :: Real ( ref filename) = filename
1081- && let Ok ( cur_dir) = env:: current_dir ( )
1082- && let Some ( local_path) = filename. local_path ( )
1083- && let Ok ( path) = local_path. strip_prefix ( & cur_dir)
1084- {
1085- return path. to_owned ( ) . into ( ) ;
1032+ fn add_test ( & mut self , test : ScrapedDoctest ) {
1033+ let ( filename, line, logical_path, langstr, text) = match test {
1034+ ScrapedDoctest :: Rust ( RustDoctest { filename, line, logical_path, langstr, text } ) => {
1035+ ( filename, line, logical_path, langstr, text)
10861036 }
1087- filename
1088- } else if let Some ( ref filename) = self . filename {
1089- filename. clone ( ) . into ( )
1090- } else {
1091- FileName :: Custom ( "input" . to_owned ( ) )
1092- }
1093- }
1094- }
1037+ ScrapedDoctest :: Markdown ( MdDoctest { filename, line, logical_path, langstr, text } ) => {
1038+ ( filename, line, logical_path, langstr, text)
1039+ }
1040+ } ;
10951041
1096- impl DoctestVisitor for Collector {
1097- fn visit_test ( & mut self , test : String , config : LangString , line : usize ) {
1098- let filename = self . get_filename ( ) ;
1099- let name = self . generate_name ( line, & filename) ;
1042+ let name = self . generate_name ( & filename, line, & logical_path) ;
11001043 let crate_name = self . crate_name . clone ( ) ;
11011044 let opts = self . opts . clone ( ) ;
1102- let edition = config . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1045+ let edition = langstr . edition . unwrap_or ( self . rustdoc_options . edition ) ;
11031046 let target_str = self . rustdoc_options . target . to_string ( ) ;
11041047 let unused_externs = self . unused_extern_reports . clone ( ) ;
1105- let no_run = config . no_run || self . rustdoc_options . no_run ;
1106- if !config . compile_fail {
1048+ let no_run = langstr . no_run || self . rustdoc_options . no_run ;
1049+ if !langstr . compile_fail {
11071050 self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
11081051 }
11091052
@@ -1140,11 +1083,11 @@ impl DoctestVisitor for Collector {
11401083 let rustdoc_test_options =
11411084 IndividualTestOptions :: new ( & self . rustdoc_options , & self . arg_file , test_id) ;
11421085
1143- debug ! ( "creating test {name}: {test }" ) ;
1086+ debug ! ( "creating test {name}: {text }" ) ;
11441087 self . tests . push ( test:: TestDescAndFn {
11451088 desc : test:: TestDesc {
11461089 name : test:: DynTestName ( name) ,
1147- ignore : match config . ignore {
1090+ ignore : match langstr . ignore {
11481091 Ignore :: All => true ,
11491092 Ignore :: None => false ,
11501093 Ignore :: Some ( ref ignores) => ignores. iter ( ) . any ( |s| target_str. contains ( s) ) ,
@@ -1157,7 +1100,7 @@ impl DoctestVisitor for Collector {
11571100 end_col : 0 ,
11581101 // compiler failures are test failures
11591102 should_panic : test:: ShouldPanic :: No ,
1160- compile_fail : config . compile_fail ,
1103+ compile_fail : langstr . compile_fail ,
11611104 no_run,
11621105 test_type : test:: TestType :: DocTest ,
11631106 } ,
@@ -1166,11 +1109,11 @@ impl DoctestVisitor for Collector {
11661109 unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
11671110 } ;
11681111 let res = run_test (
1169- & test ,
1112+ & text ,
11701113 & crate_name,
11711114 line,
11721115 rustdoc_test_options,
1173- config ,
1116+ langstr ,
11741117 no_run,
11751118 & opts,
11761119 edition,
@@ -1233,59 +1176,6 @@ impl DoctestVisitor for Collector {
12331176 } ) ) ,
12341177 } ) ;
12351178 }
1236-
1237- fn get_line ( & self ) -> usize {
1238- if let Some ( ref source_map) = self . source_map {
1239- let line = self . position . lo ( ) . to_usize ( ) ;
1240- let line = source_map. lookup_char_pos ( BytePos ( line as u32 ) ) . line ;
1241- if line > 0 { line - 1 } else { line }
1242- } else {
1243- 0
1244- }
1245- }
1246-
1247- fn visit_header ( & mut self , name : & str , level : u32 ) {
1248- if self . use_headers {
1249- // We use these headings as test names, so it's good if
1250- // they're valid identifiers.
1251- let name = name
1252- . chars ( )
1253- . enumerate ( )
1254- . map ( |( i, c) | {
1255- if ( i == 0 && rustc_lexer:: is_id_start ( c) )
1256- || ( i != 0 && rustc_lexer:: is_id_continue ( c) )
1257- {
1258- c
1259- } else {
1260- '_'
1261- }
1262- } )
1263- . collect :: < String > ( ) ;
1264-
1265- // Here we try to efficiently assemble the header titles into the
1266- // test name in the form of `h1::h2::h3::h4::h5::h6`.
1267- //
1268- // Suppose that originally `self.names` contains `[h1, h2, h3]`...
1269- let level = level as usize ;
1270- if level <= self . names . len ( ) {
1271- // ... Consider `level == 2`. All headers in the lower levels
1272- // are irrelevant in this new level. So we should reset
1273- // `self.names` to contain headers until <h2>, and replace that
1274- // slot with the new name: `[h1, name]`.
1275- self . names . truncate ( level) ;
1276- self . names [ level - 1 ] = name;
1277- } else {
1278- // ... On the other hand, consider `level == 5`. This means we
1279- // need to extend `self.names` to contain five headers. We fill
1280- // in the missing level (<h4>) with `_`. Thus `self.names` will
1281- // become `[h1, h2, h3, "_", name]`.
1282- if level - 1 > self . names . len ( ) {
1283- self . names . resize ( level - 1 , "_" . to_owned ( ) ) ;
1284- }
1285- self . names . push ( name) ;
1286- }
1287- }
1288- }
12891179}
12901180
12911181#[ cfg( test) ] // used in tests
0 commit comments