diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 1ba7ce953b5d4..9b1f43156522e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -391,7 +391,7 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option { let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( "when UNSTABLE_RUSTDOC_TEST_PATH is set \ @@ -400,12 +400,13 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option() .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be a number"); - FileName::doc_test_source_code(PathBuf::from(path), line) + Some(Input::DocTestStr { file: PathBuf::from(path), line, input }) } - Err(_) => FileName::anon_source_code(&input), - }; - - Some(Input::Str { name, input }) + Err(_) => { + let name = FileName::anon_source_code(&input); + Some(Input::Str { name, input }) + } + } } [ifile] => Some(Input::File(PathBuf::from(ifile))), [ifile1, ifile2, ..] => early_dcx.early_fatal(format!( @@ -619,7 +620,7 @@ fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { } safe_println!("{}", String::from_utf8(v).unwrap()); } - Input::Str { .. } => { + Input::Str { .. } | Input::DocTestStr { .. } => { sess.dcx().fatal("cannot list metadata for stdin"); } } @@ -1373,12 +1374,14 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { Input::File(file) => { new_parser_from_file(&sess.psess, file, StripTokens::ShebangAndFrontmatter, None) } - Input::Str { name, input } => new_parser_from_source_str( - &sess.psess, - name.clone(), - input.clone(), - StripTokens::ShebangAndFrontmatter, - ), + instr @ (Input::Str { input, .. } | Input::DocTestStr { input, .. }) => { + new_parser_from_source_str( + &sess.psess, + instr.file_name(sess), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ) + } }); parser.parse_inner_attributes() } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 5ea1cb20990b5..cdcc12bcbbd7a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -61,12 +61,14 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { StripTokens::ShebangAndFrontmatter, None, ), - Input::Str { input, name } => new_parser_from_source_str( - &sess.psess, - name.clone(), - input.clone(), - StripTokens::ShebangAndFrontmatter, - ), + instr @ (Input::Str { input, .. } | Input::DocTestStr { input, .. }) => { + new_parser_from_source_str( + &sess.psess, + instr.file_name(sess), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ) + } }); parser.parse_crate_mod() }) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fdfc2aacc79a8..65349f48fcfb9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1064,6 +1064,15 @@ impl Default for NextSolverConfig { pub enum Input { /// Load source code from a file. File(PathBuf), + /// Load source code from a doctest string. + DocTestStr { + /// The file associated with this input. + file: PathBuf, + // Line associated with this input + line: isize, + /// An anonymous string containing the source code. + input: String, + }, /// Load source code from a string. Str { /// A string that is shown in place of a filename. @@ -1094,6 +1103,14 @@ impl Input { .path_mapping() .to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()), ), + Input::DocTestStr { ref file, line, .. } => FileName::DocTest( + session + .psess + .source_map() + .path_mapping() + .to_real_filename(session.psess.source_map().working_dir(), file.as_path()), + line, + ), Input::Str { ref name, .. } => name.clone(), } } @@ -1101,6 +1118,7 @@ impl Input { pub fn opt_path(&self) -> Option<&Path> { match self { Input::File(file) => Some(file), + Input::DocTestStr { file, .. } => Some(file), Input::Str { name, .. } => match name { FileName::Real(real) => real.local_path(), FileName::CfgSpec(_) => None, @@ -1109,7 +1127,7 @@ impl Input { FileName::ProcMacroSourceCode(_) => None, FileName::CliCrateAttr(_) => None, FileName::Custom(_) => None, - FileName::DocTest(path, _) => Some(path), + FileName::DocTest(path, _) => path.local_path(), FileName::InlineAsm(_) => None, }, } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ddec29eca6517..088f4c7216cbd 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -520,7 +520,8 @@ pub enum FileName { CliCrateAttr(Hash64), /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), - DocTest(PathBuf, isize), + /// Custom path for doctests + DocTest(RealFileName, isize), /// Post-substitution inline assembly from LLVM. InlineAsm(Hash64), } @@ -543,7 +544,7 @@ impl fmt::Display for FileNameDisplay<'_> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use FileName::*; match *self.inner { - Real(ref name) => { + Real(ref name) | DocTest(ref name, _) => { write!(fmt, "{}", name.to_string_lossy(self.display_pref)) } CfgSpec(_) => write!(fmt, ""), @@ -552,7 +553,6 @@ impl fmt::Display for FileNameDisplay<'_> { ProcMacroSourceCode(_) => write!(fmt, ""), CliCrateAttr(_) => write!(fmt, ""), Custom(ref s) => write!(fmt, "<{s}>"), - DocTest(ref path, _) => write!(fmt, "{}", path.display()), InlineAsm(_) => write!(fmt, ""), } } @@ -643,7 +643,7 @@ impl FileName { FileName::CliCrateAttr(hasher.finish()) } - pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { + pub fn doc_test_source_code(path: RealFileName, line: isize) -> FileName { FileName::DocTest(path, line) } @@ -660,7 +660,7 @@ impl FileName { pub fn into_local_path(self) -> Option { match self { FileName::Real(path) => path.into_local_path(), - FileName::DocTest(path, _) => Some(path), + FileName::DocTest(path, _) => path.into_local_path(), _ => None, } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 170352dabf656..9134b63dc5032 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -129,8 +129,10 @@ pub(crate) struct Options { pub(crate) no_run: bool, /// What sources are being mapped. pub(crate) remap_path_prefix: Vec<(PathBuf, PathBuf)>, + pub(crate) remap_path_prefix_strs: Vec, /// Which scope(s) to use with `--remap-path-prefix` pub(crate) remap_path_scope: RemapPathScopeComponents, + pub(crate) remap_path_scope_str: Option, /// The path to a rustc-like binary to build tests with. If not set, we /// default to loading from `$sysroot/bin/rustc`. @@ -859,6 +861,8 @@ impl Options { let unstable_opts_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); + let remap_path_prefix_strs = matches.opt_strs("remap-path-prefix"); + let remap_path_scope_str = matches.opt_str("remap-path-scope"); let test_runtool = matches.opt_str("test-runtool"); let test_runtool_args = matches.opt_strs("test-runtool-arg"); let document_private = matches.opt_present("document-private-items"); @@ -934,7 +938,9 @@ impl Options { no_run, test_builder_wrappers, remap_path_prefix, + remap_path_prefix_strs, remap_path_scope, + remap_path_scope_str, no_capture, crate_name, output_format, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c8d1cfd599909..35b2af5370fff 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -103,6 +103,13 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> content.push(format!("--check-cfg={check_cfg}")); } + for prefix in &options.remap_path_prefix_strs { + content.push(format!("--remap-path-prefix={prefix}")); + } + if let Some(scope) = &options.remap_path_scope_str { + content.push(format!("--remap-path-scope={scope}")); + } + for lib_str in &options.lib_strs { content.push(format!("-L{lib_str}")); } @@ -986,7 +993,7 @@ impl ScrapedDocTest { } let name = format!( "{} - {item_path}(line {line})", - filename.display(RemapPathScopeComponents::DOCUMENTATION) + filename.display(RemapPathScopeComponents::DIAGNOSTICS) ); Self { filename, line, langstr, text, name, span, code_mappings, global_crate_attrs } @@ -999,13 +1006,12 @@ impl ScrapedDocTest { self.langstr.no_run || opts.no_run } - fn path(&self) -> PathBuf { - match &self.filename { - FileName::Real(name) => { - name.path(RemapPathScopeComponents::DOCUMENTATION).to_path_buf() - } - _ => PathBuf::from(r"doctest.rs"), - } + fn path(&self, scope: RemapPathScopeComponents) -> String { + self.filename.display(scope).to_string() + } + + fn local_path(&self) -> PathBuf { + self.filename.clone().into_local_path().unwrap_or_else(|| PathBuf::from(r"doctest.rs")) } } @@ -1138,7 +1144,7 @@ fn generate_test_desc_and_fn( ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); let rustdoc_test_options = - IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.path()); + IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.local_path()); debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a994f1d974ae3..05296b791709d 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -94,7 +94,7 @@ pub(crate) fn test(input: &Input, options: Options, dcx: DiagCtxtHandle<'_>) -> Input::File(path) => { read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))? } - Input::Str { name: _, input } => input.clone(), + Input::Str { name: _, input } | Input::DocTestStr { input, .. } => input.clone(), }; // Obviously not a real crate name, but close enough for purposes of doctests. diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 43ed48aad95d4..3e4b4209f4145 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use std::time::Duration; use rustc_data_structures::fx::FxIndexSet; +use rustc_span::RemapPathScopeComponents; use rustc_span::edition::Edition; use crate::doctest::{ @@ -264,7 +265,7 @@ test::StaticTestFn( )); }}", test_name = scraped_test.name, - file = scraped_test.path(), + file = scraped_test.path(RemapPathScopeComponents::MACRO), line = scraped_test.line, no_run = scraped_test.langstr.no_run, should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic, diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout b/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout index 22f4fe70c6175..248f7734652fa 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED +test remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED failures: ----- $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- +---- remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- error: this file contains an unclosed delimiter - --> $DIR/remap-path-prefix-doctest.rs:20:12 + --> remapped_path/remap-path-prefix-doctest.rs:20:12 | LL | fn invalid( | -^ @@ -18,7 +18,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) + remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout b/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout index 248f7734652fa..22f4fe70c6175 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout @@ -1,12 +1,12 @@ running 1 test -test remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED +test $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED failures: ----- remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- +---- $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- error: this file contains an unclosed delimiter - --> remapped_path/remap-path-prefix-doctest.rs:20:12 + --> $DIR/remap-path-prefix-doctest.rs:20:12 | LL | fn invalid( | -^ @@ -18,7 +18,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) + $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout b/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.rs b/tests/rustdoc-ui/remap-path-prefix-file.rs new file mode 100644 index 0000000000000..8ec5c88b0b7d6 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.rs @@ -0,0 +1,37 @@ +// Checks that macros uses inside doctests are correctly gated under the +// `macro` and al --remap-path-scope scopes. + +//@ only-linux +//@ compile-flags: --test --test-args=--test-threads=1 +//@ compile-flags: --remap-path-prefix={{src-base}}=/REMAPPED +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ rustc-env: RUST_BACKTRACE=0 +//@ check-pass + +//@ revisions: macro_scope diagnostics_scope documentation_scope debuginfo_scope +//@ revisions: object_scope all_scope no_scopes + +//@[macro_scope] compile-flags: --remap-path-scope=macro -Z unstable-options +//@[diagnostics_scope] compile-flags: --remap-path-scope=diagnostics -Z unstable-options +//@[documentation_scope] compile-flags: --remap-path-scope=documentation -Z unstable-options +//@[debuginfo_scope] compile-flags: --remap-path-scope=debuginfo -Z unstable-options +//@[object_scope] compile-flags: --remap-path-scope=object -Z unstable-options +//@[all_scope] compile-flags: --remap-path-scope=all -Z unstable-options +// `no_scopes` passes no --remap-path-scope, so defaults to `all` + +/// Only `macro`, `object` and `all` should remap what `file!()` expands to. +/// +/// ``` +/// #[cfg(any(macro_scope, object_scope, all_scope, no_scopes))] +/// const EXPECT_REMAPPED: bool = true; +/// #[cfg(not(any(macro_scope, object_scope, all_scope, no_scopes)))] +/// const EXPECT_REMAPPED: bool = false; +/// +/// let file = file!(); +/// assert_eq!( +/// file.starts_with("/REMAPPED"), +/// EXPECT_REMAPPED, +/// "unexpected file!() = {file}", +/// ); +/// ``` +pub fn f() {}