Merge from rustc
This commit is contained in:
commit
b65cdffbe4
675 changed files with 9247 additions and 5422 deletions
|
|
@ -11,13 +11,19 @@ use std::str::FromStr;
|
|||
use std::{env, process};
|
||||
|
||||
use bootstrap::{
|
||||
find_recent_config_change_ids, human_readable_changes, t, Build, Config, Subcommand,
|
||||
find_recent_config_change_ids, human_readable_changes, t, Build, Config, Flags, Subcommand,
|
||||
CONFIG_CHANGE_HISTORY,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let config = Config::parse(&args);
|
||||
|
||||
if Flags::try_parse_verbose_help(&args) {
|
||||
return;
|
||||
}
|
||||
|
||||
let flags = Flags::parse(&args);
|
||||
let config = Config::parse(flags);
|
||||
|
||||
let mut build_lock;
|
||||
let _build_lock_guard;
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
|
|||
|
||||
fn clean_specific_stage(build: &Build, stage: u32) {
|
||||
for host in &build.hosts {
|
||||
let entries = match build.out.join(host.triple).read_dir() {
|
||||
let entries = match build.out.join(host).read_dir() {
|
||||
Ok(iter) => iter,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
|
@ -148,7 +148,7 @@ fn clean_default(build: &Build) {
|
|||
rm_rf(&build.out.join("bootstrap-shims-dump"));
|
||||
rm_rf(&build.out.join("rustfmt.stamp"));
|
||||
|
||||
let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t.triple)).collect();
|
||||
let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect();
|
||||
// After cross-compilation, artifacts of the host architecture (which may differ from build.host)
|
||||
// might not get removed.
|
||||
// Adding its path (linked one for easier accessibility) will solve this problem.
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ impl Step for Std {
|
|||
.rustc_snapshot_sysroot()
|
||||
.join("lib")
|
||||
.join("rustlib")
|
||||
.join(compiler.host.triple)
|
||||
.join(compiler.host)
|
||||
.join("bin");
|
||||
if src_sysroot_bin.exists() {
|
||||
let target_sysroot_bin =
|
||||
|
|
@ -432,7 +432,7 @@ fn copy_self_contained_objects(
|
|||
DependencyType::TargetSelfContained,
|
||||
);
|
||||
}
|
||||
} else if target.ends_with("windows-gnu") {
|
||||
} else if target.is_windows_gnu() {
|
||||
for obj in ["crt2.o", "dllcrt2.o"].iter() {
|
||||
let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj);
|
||||
let target = libdir_self_contained.join(obj);
|
||||
|
|
@ -651,8 +651,8 @@ impl Step for StdLink {
|
|||
compiler: self.compiler,
|
||||
force_recompile: self.force_recompile,
|
||||
});
|
||||
let libdir = sysroot.join(lib).join("rustlib").join(target.triple).join("lib");
|
||||
let hostdir = sysroot.join(lib).join("rustlib").join(compiler.host.triple).join("lib");
|
||||
let libdir = sysroot.join(lib).join("rustlib").join(target).join("lib");
|
||||
let hostdir = sysroot.join(lib).join("rustlib").join(compiler.host).join("lib");
|
||||
(libdir, hostdir)
|
||||
} else {
|
||||
let libdir = builder.sysroot_libdir(target_compiler, target);
|
||||
|
|
@ -670,12 +670,12 @@ impl Step for StdLink {
|
|||
.build
|
||||
.config
|
||||
.initial_rustc
|
||||
.starts_with(builder.out.join(compiler.host.triple).join("stage0/bin"))
|
||||
.starts_with(builder.out.join(compiler.host).join("stage0/bin"))
|
||||
{
|
||||
// Copy bin files from stage0/bin to stage0-sysroot/bin
|
||||
let sysroot = builder.out.join(compiler.host.triple).join("stage0-sysroot");
|
||||
let sysroot = builder.out.join(compiler.host).join("stage0-sysroot");
|
||||
|
||||
let host = compiler.host.triple;
|
||||
let host = compiler.host;
|
||||
let stage0_bin_dir = builder.out.join(host).join("stage0/bin");
|
||||
let sysroot_bin_dir = sysroot.join("bin");
|
||||
t!(fs::create_dir_all(&sysroot_bin_dir));
|
||||
|
|
@ -793,7 +793,7 @@ impl Step for StartupObjects {
|
|||
fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> {
|
||||
let for_compiler = self.compiler;
|
||||
let target = self.target;
|
||||
if !target.ends_with("windows-gnu") {
|
||||
if !target.is_windows_gnu() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
|
|
@ -1554,7 +1554,7 @@ impl Step for Sysroot {
|
|||
/// For all other stages, it's the same stage directory that the compiler lives in.
|
||||
fn run(self, builder: &Builder<'_>) -> PathBuf {
|
||||
let compiler = self.compiler;
|
||||
let host_dir = builder.out.join(compiler.host.triple);
|
||||
let host_dir = builder.out.join(compiler.host);
|
||||
|
||||
let sysroot_dir = |stage| {
|
||||
if stage == 0 {
|
||||
|
|
|
|||
|
|
@ -275,12 +275,8 @@ fn make_win_dist(
|
|||
}
|
||||
|
||||
//Copy platform tools to platform-specific bin directory
|
||||
let target_bin_dir = plat_root
|
||||
.join("lib")
|
||||
.join("rustlib")
|
||||
.join(target.triple)
|
||||
.join("bin")
|
||||
.join("self-contained");
|
||||
let target_bin_dir =
|
||||
plat_root.join("lib").join("rustlib").join(target).join("bin").join("self-contained");
|
||||
fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed");
|
||||
for src in target_tools {
|
||||
builder.copy_link_to_folder(&src, &target_bin_dir);
|
||||
|
|
@ -295,12 +291,8 @@ fn make_win_dist(
|
|||
);
|
||||
|
||||
//Copy platform libs to platform-specific lib directory
|
||||
let target_lib_dir = plat_root
|
||||
.join("lib")
|
||||
.join("rustlib")
|
||||
.join(target.triple)
|
||||
.join("lib")
|
||||
.join("self-contained");
|
||||
let target_lib_dir =
|
||||
plat_root.join("lib").join("rustlib").join(target).join("lib").join("self-contained");
|
||||
fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed");
|
||||
for src in target_libs {
|
||||
builder.copy_link_to_folder(&src, &target_lib_dir);
|
||||
|
|
@ -450,7 +442,7 @@ impl Step for Rustc {
|
|||
// component for now.
|
||||
maybe_install_llvm_runtime(builder, host, image);
|
||||
|
||||
let dst_dir = image.join("lib/rustlib").join(&*host.triple).join("bin");
|
||||
let dst_dir = image.join("lib/rustlib").join(host).join("bin");
|
||||
t!(fs::create_dir_all(&dst_dir));
|
||||
|
||||
// Copy over lld if it's there
|
||||
|
|
@ -607,7 +599,7 @@ fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp
|
|||
|
||||
/// Copy stamped files into an image's `target/lib` directory.
|
||||
fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) {
|
||||
let dst = image.join("lib/rustlib").join(target.triple).join("lib");
|
||||
let dst = image.join("lib/rustlib").join(target).join("lib");
|
||||
let self_contained_dst = dst.join("self-contained");
|
||||
t!(fs::create_dir_all(&dst));
|
||||
t!(fs::create_dir_all(&self_contained_dst));
|
||||
|
|
@ -769,7 +761,7 @@ impl Step for Analysis {
|
|||
|
||||
let src = builder
|
||||
.stage_out(compiler, Mode::Std)
|
||||
.join(target.triple)
|
||||
.join(target)
|
||||
.join(builder.cargo_dir())
|
||||
.join("deps")
|
||||
.join("save-analysis");
|
||||
|
|
@ -1509,7 +1501,7 @@ impl Step for Extended {
|
|||
tarballs.push(builder.ensure(Rustc { compiler: builder.compiler(stage, target) }));
|
||||
tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
|
||||
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
|
||||
}
|
||||
|
||||
|
|
@ -1683,7 +1675,7 @@ impl Step for Extended {
|
|||
prepare(tool);
|
||||
}
|
||||
}
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
prepare("rust-mingw");
|
||||
}
|
||||
|
||||
|
|
@ -1830,7 +1822,7 @@ impl Step for Extended {
|
|||
.arg("-t")
|
||||
.arg(etc.join("msi/remove-duplicates.xsl"))
|
||||
.run(builder);
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
command(&heat)
|
||||
.current_dir(&exe)
|
||||
.arg("dir")
|
||||
|
|
@ -1876,7 +1868,7 @@ impl Step for Extended {
|
|||
if built_tools.contains("miri") {
|
||||
cmd.arg("-dMiriDir=miri");
|
||||
}
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
cmd.arg("-dGccDir=rust-mingw");
|
||||
}
|
||||
cmd.run(builder);
|
||||
|
|
@ -1901,7 +1893,7 @@ impl Step for Extended {
|
|||
}
|
||||
candle("AnalysisGroup.wxs".as_ref());
|
||||
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
candle("GccGroup.wxs".as_ref());
|
||||
}
|
||||
|
||||
|
|
@ -1941,7 +1933,7 @@ impl Step for Extended {
|
|||
cmd.arg("DocsGroup.wixobj");
|
||||
}
|
||||
|
||||
if target.ends_with("windows-gnu") {
|
||||
if target.is_windows_gnu() {
|
||||
cmd.arg("GccGroup.wixobj");
|
||||
}
|
||||
// ICE57 wrongly complains about the shortcuts
|
||||
|
|
@ -1973,7 +1965,7 @@ fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSele
|
|||
|
||||
if target.contains("windows-gnullvm") {
|
||||
cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
|
||||
} else if target.contains("windows-gnu") {
|
||||
} else if target.is_windows_gnu() {
|
||||
cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
|
||||
} else {
|
||||
cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
|
||||
|
|
@ -2087,7 +2079,7 @@ fn maybe_install_llvm(
|
|||
|
||||
/// Maybe add libLLVM.so to the target lib-dir for linking.
|
||||
pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
|
||||
let dst_libdir = sysroot.join("lib/rustlib").join(&*target.triple).join("lib");
|
||||
let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
|
||||
// We do not need to copy LLVM files into the sysroot if it is not
|
||||
// dynamically linked; it is already included into librustc_llvm
|
||||
// statically.
|
||||
|
|
|
|||
|
|
@ -699,13 +699,12 @@ fn doc_std(
|
|||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
|
||||
let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
|
||||
let target_dir =
|
||||
builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name);
|
||||
let target_dir = builder.stage_out(compiler, Mode::Std).join(target).join(target_doc_dir_name);
|
||||
|
||||
// This is directory where the compiler will place the output of the command.
|
||||
// We will then copy the files from this directory into the final `out` directory, the specified
|
||||
// as a function parameter.
|
||||
let out_dir = target_dir.join(target.triple).join("doc");
|
||||
let out_dir = target_dir.join(target).join("doc");
|
||||
|
||||
let mut cargo =
|
||||
builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, Kind::Doc);
|
||||
|
|
@ -846,7 +845,7 @@ impl Step for Rustc {
|
|||
|
||||
let mut to_open = None;
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
|
||||
let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target).join("doc");
|
||||
for krate in &*self.crates {
|
||||
// Create all crate output directories first to make sure rustdoc uses
|
||||
// relative links.
|
||||
|
|
@ -992,7 +991,7 @@ macro_rules! tool_doc {
|
|||
// see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
|
||||
// cargo.rustdocflag("--generate-link-to-definition");
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
|
||||
let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target).join("doc");
|
||||
$(for krate in $crates {
|
||||
let dir_name = krate.replace("-", "_");
|
||||
t!(fs::create_dir_all(out_dir.join(&*dir_name)));
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ You can skip linkcheck with --skip src/tools/linkchecker"
|
|||
let _guard =
|
||||
builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host);
|
||||
let _time = helpers::timeit(builder);
|
||||
linkchecker.delay_failure().arg(builder.out.join(host.triple).join("doc")).run(builder);
|
||||
linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder);
|
||||
}
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
|
|
@ -434,8 +434,8 @@ impl Miri {
|
|||
builder: &Builder<'_>,
|
||||
compiler: Compiler,
|
||||
target: TargetSelection,
|
||||
) -> String {
|
||||
let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot");
|
||||
) -> PathBuf {
|
||||
let miri_sysroot = builder.out.join(compiler.host).join("miri-sysroot");
|
||||
let mut cargo = builder::Cargo::new(
|
||||
builder,
|
||||
compiler,
|
||||
|
|
@ -467,7 +467,7 @@ impl Miri {
|
|||
// Output is "<sysroot>\n".
|
||||
let sysroot = stdout.trim_end();
|
||||
builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
|
||||
sysroot.to_owned()
|
||||
PathBuf::from(sysroot)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -520,12 +520,14 @@ impl Step for Miri {
|
|||
builder.ensure(compile::Std::new(target_compiler, host));
|
||||
let host_sysroot = builder.sysroot(target_compiler);
|
||||
|
||||
// Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared
|
||||
// properly when rustc changes. Similar to `Builder::cargo`, we skip this in dry runs to
|
||||
// make sure the relevant compiler has been set up properly.
|
||||
// Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when
|
||||
// the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors.
|
||||
if !builder.config.dry_run() {
|
||||
let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("miri_ui");
|
||||
builder.clear_if_dirty(&ui_test_dep_dir, &builder.rustc(host_compiler));
|
||||
// The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see
|
||||
// <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>).
|
||||
// We can hence use that directly as a signal to clear the ui test dir.
|
||||
builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot);
|
||||
}
|
||||
|
||||
// Run `cargo test`.
|
||||
|
|
@ -1113,7 +1115,7 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
|
|||
}
|
||||
|
||||
fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {
|
||||
builder.out.join(host.triple).join("test")
|
||||
builder.out.join(host).join("test")
|
||||
}
|
||||
|
||||
macro_rules! default_test {
|
||||
|
|
@ -1815,7 +1817,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
|
|||
|
||||
let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
|
||||
flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
|
||||
flags.extend(builder.config.cmd.rustc_args().iter().map(|s| s.to_string()));
|
||||
flags.extend(builder.config.cmd.compiletest_rustc_args().iter().map(|s| s.to_string()));
|
||||
|
||||
if suite != "mir-opt" {
|
||||
if let Some(linker) = builder.linker(target) {
|
||||
|
|
@ -2683,7 +2685,7 @@ impl Step for Crate {
|
|||
if builder.download_rustc() && compiler.stage > 0 {
|
||||
let sysroot = builder
|
||||
.out
|
||||
.join(compiler.host.triple)
|
||||
.join(compiler.host)
|
||||
.join(format!("stage{}-test-sysroot", compiler.stage));
|
||||
cargo.env("RUSTC_SYSROOT", sysroot);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1171,7 +1171,7 @@ impl<'a> Builder<'a> {
|
|||
.sysroot(self.compiler)
|
||||
.join(lib)
|
||||
.join("rustlib")
|
||||
.join(self.target.triple)
|
||||
.join(self.target)
|
||||
.join("lib");
|
||||
// Avoid deleting the rustlib/ directory we just copied
|
||||
// (in `impl Step for Sysroot`).
|
||||
|
|
@ -1254,7 +1254,7 @@ impl<'a> Builder<'a> {
|
|||
|
||||
// Ensure that the downloaded LLVM libraries can be found.
|
||||
if self.config.llvm_from_ci {
|
||||
let ci_llvm_lib = self.out.join(&*compiler.host.triple).join("ci-llvm").join("lib");
|
||||
let ci_llvm_lib = self.out.join(compiler.host).join("ci-llvm").join("lib");
|
||||
dylib_dirs.push(ci_llvm_lib);
|
||||
}
|
||||
|
||||
|
|
@ -1504,9 +1504,9 @@ impl<'a> Builder<'a> {
|
|||
Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
|
||||
Mode::Std => {
|
||||
if self.config.cmd.json() {
|
||||
out_dir.join(target.triple).join("json-doc")
|
||||
out_dir.join(target).join("json-doc")
|
||||
} else {
|
||||
out_dir.join(target.triple).join("doc")
|
||||
out_dir.join(target).join("doc")
|
||||
}
|
||||
}
|
||||
_ => panic!("doc mode {mode:?} not expected"),
|
||||
|
|
@ -2226,11 +2226,6 @@ impl<'a> Builder<'a> {
|
|||
rustdocflags.arg("--cfg=parallel_compiler");
|
||||
}
|
||||
|
||||
// Pass the value of `--rustc-args` from test command. If it's not a test command, this won't set anything.
|
||||
self.config.cmd.rustc_args().iter().for_each(|v| {
|
||||
rustflags.arg(v);
|
||||
});
|
||||
|
||||
Cargo {
|
||||
command: cargo,
|
||||
compiler,
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ use std::thread;
|
|||
use super::*;
|
||||
use crate::core::build_steps::doc::DocumentationFormat;
|
||||
use crate::core::config::Config;
|
||||
use crate::Flags;
|
||||
|
||||
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
|
||||
configure_with_args(&[cmd.to_owned()], host, target)
|
||||
}
|
||||
|
||||
fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config {
|
||||
let mut config = Config::parse(cmd);
|
||||
let mut config = Config::parse(Flags::parse(cmd));
|
||||
// don't save toolstates
|
||||
config.save_toolstates = None;
|
||||
config.dry_run = DryRun::SelfCheck;
|
||||
|
|
@ -23,7 +24,7 @@ fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config
|
|||
let submodule_build = Build::new(Config {
|
||||
// don't include LLVM, so CI doesn't require ninja/cmake to be installed
|
||||
rust_codegen_backends: vec![],
|
||||
..Config::parse(&["check".to_owned()])
|
||||
..Config::parse(Flags::parse(&["check".to_owned()]))
|
||||
});
|
||||
submodule_build.require_submodule("src/doc/book", None);
|
||||
config.submodules = Some(false);
|
||||
|
|
@ -632,7 +633,7 @@ mod dist {
|
|||
config.paths = vec!["library/std".into()];
|
||||
config.cmd = Subcommand::Test {
|
||||
test_args: vec![],
|
||||
rustc_args: vec![],
|
||||
compiletest_rustc_args: vec![],
|
||||
no_fail_fast: false,
|
||||
no_doc: true,
|
||||
doc: false,
|
||||
|
|
@ -703,7 +704,7 @@ mod dist {
|
|||
let mut config = configure(&["A-A"], &["A-A"]);
|
||||
config.cmd = Subcommand::Test {
|
||||
test_args: vec![],
|
||||
rustc_args: vec![],
|
||||
compiletest_rustc_args: vec![],
|
||||
no_fail_fast: false,
|
||||
doc: true,
|
||||
no_doc: false,
|
||||
|
|
|
|||
|
|
@ -514,6 +514,10 @@ impl TargetSelection {
|
|||
self.contains("windows")
|
||||
}
|
||||
|
||||
pub fn is_windows_gnu(&self) -> bool {
|
||||
self.ends_with("windows-gnu")
|
||||
}
|
||||
|
||||
/// Path to the file defining the custom target, if any.
|
||||
pub fn filepath(&self) -> Option<&Path> {
|
||||
self.file.as_ref().map(Path::new)
|
||||
|
|
@ -542,6 +546,14 @@ impl PartialEq<&str> for TargetSelection {
|
|||
}
|
||||
}
|
||||
|
||||
// Targets are often used as directory names throughout bootstrap.
|
||||
// This impl makes it more ergonomics to use them as such.
|
||||
impl AsRef<Path> for TargetSelection {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.triple.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-target configuration stored in the global configuration structure.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct Target {
|
||||
|
|
@ -1191,7 +1203,7 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(args: &[String]) -> Config {
|
||||
pub fn parse(flags: Flags) -> Config {
|
||||
#[cfg(test)]
|
||||
fn get_toml(_: &Path) -> TomlConfig {
|
||||
TomlConfig::default()
|
||||
|
|
@ -1221,11 +1233,10 @@ impl Config {
|
|||
exit!(2);
|
||||
})
|
||||
}
|
||||
Self::parse_inner(args, get_toml)
|
||||
Self::parse_inner(flags, get_toml)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_inner(args: &[String], get_toml: impl Fn(&Path) -> TomlConfig) -> Config {
|
||||
let mut flags = Flags::parse(args);
|
||||
pub(crate) fn parse_inner(mut flags: Flags, get_toml: impl Fn(&Path) -> TomlConfig) -> Config {
|
||||
let mut config = Config::default_opts();
|
||||
|
||||
// Set flags.
|
||||
|
|
@ -1470,7 +1481,7 @@ impl Config {
|
|||
config.download_beta_toolchain();
|
||||
config
|
||||
.out
|
||||
.join(config.build.triple)
|
||||
.join(config.build)
|
||||
.join("stage0")
|
||||
.join("bin")
|
||||
.join(exe("rustc", config.build))
|
||||
|
|
@ -1485,7 +1496,7 @@ impl Config {
|
|||
config.download_beta_toolchain();
|
||||
config
|
||||
.out
|
||||
.join(config.build.triple)
|
||||
.join(config.build)
|
||||
.join("stage0")
|
||||
.join("bin")
|
||||
.join(exe("cargo", config.build))
|
||||
|
|
@ -2278,13 +2289,13 @@ impl Config {
|
|||
/// The absolute path to the downloaded LLVM artifacts.
|
||||
pub(crate) fn ci_llvm_root(&self) -> PathBuf {
|
||||
assert!(self.llvm_from_ci);
|
||||
self.out.join(&*self.build.triple).join("ci-llvm")
|
||||
self.out.join(self.build).join("ci-llvm")
|
||||
}
|
||||
|
||||
/// Directory where the extracted `rustc-dev` component is stored.
|
||||
pub(crate) fn ci_rustc_dir(&self) -> PathBuf {
|
||||
assert!(self.download_rustc());
|
||||
self.out.join(self.build.triple).join("ci-rustc")
|
||||
self.out.join(self.build).join("ci-rustc")
|
||||
}
|
||||
|
||||
/// Determine whether llvm should be linked dynamically.
|
||||
|
|
|
|||
|
|
@ -183,9 +183,9 @@ pub struct Flags {
|
|||
}
|
||||
|
||||
impl Flags {
|
||||
pub fn parse(args: &[String]) -> Self {
|
||||
let first = String::from("x.py");
|
||||
let it = std::iter::once(&first).chain(args.iter());
|
||||
/// Check if `<cmd> -h -v` was passed.
|
||||
/// If yes, print the available paths and return `true`.
|
||||
pub fn try_parse_verbose_help(args: &[String]) -> bool {
|
||||
// We need to check for `<cmd> -h -v`, in which case we list the paths
|
||||
#[derive(Parser)]
|
||||
#[command(disable_help_flag(true))]
|
||||
|
|
@ -198,10 +198,10 @@ impl Flags {
|
|||
cmd: Kind,
|
||||
}
|
||||
if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
|
||||
HelpVerboseOnly::try_parse_from(it.clone())
|
||||
HelpVerboseOnly::try_parse_from(normalize_args(args))
|
||||
{
|
||||
println!("NOTE: updating submodules before printing available paths");
|
||||
let config = Config::parse(&[String::from("build")]);
|
||||
let config = Config::parse(Self::parse(&[String::from("build")]));
|
||||
let build = Build::new(config);
|
||||
let paths = Builder::get_help(&build, subcommand);
|
||||
if let Some(s) = paths {
|
||||
|
|
@ -209,11 +209,21 @@ impl Flags {
|
|||
} else {
|
||||
panic!("No paths available for subcommand `{}`", subcommand.as_str());
|
||||
}
|
||||
crate::exit!(0);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
Flags::parse_from(it)
|
||||
}
|
||||
|
||||
pub fn parse(args: &[String]) -> Self {
|
||||
Flags::parse_from(normalize_args(args))
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_args(args: &[String]) -> Vec<String> {
|
||||
let first = String::from("x.py");
|
||||
let it = std::iter::once(first).chain(args.iter().cloned());
|
||||
it.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, clap::Subcommand)]
|
||||
|
|
@ -347,9 +357,9 @@ pub enum Subcommand {
|
|||
/// extra arguments to be passed for the test tool being used
|
||||
/// (e.g. libtest, compiletest or rustdoc)
|
||||
test_args: Vec<String>,
|
||||
/// extra options to pass the compiler when running tests
|
||||
/// extra options to pass the compiler when running compiletest tests
|
||||
#[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
|
||||
rustc_args: Vec<String>,
|
||||
compiletest_rustc_args: Vec<String>,
|
||||
#[arg(long)]
|
||||
/// do not run doc tests
|
||||
no_doc: bool,
|
||||
|
|
@ -392,9 +402,6 @@ pub enum Subcommand {
|
|||
/// extra arguments to be passed for the test tool being used
|
||||
/// (e.g. libtest, compiletest or rustdoc)
|
||||
test_args: Vec<String>,
|
||||
/// extra options to pass the compiler when running tests
|
||||
#[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
|
||||
rustc_args: Vec<String>,
|
||||
#[arg(long)]
|
||||
/// do not run doc tests
|
||||
no_doc: bool,
|
||||
|
|
@ -499,10 +506,10 @@ impl Subcommand {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rustc_args(&self) -> Vec<&str> {
|
||||
pub fn compiletest_rustc_args(&self) -> Vec<&str> {
|
||||
match *self {
|
||||
Subcommand::Test { ref rustc_args, .. } | Subcommand::Miri { ref rustc_args, .. } => {
|
||||
rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
|
||||
Subcommand::Test { ref compiletest_rustc_args, .. } => {
|
||||
compiletest_rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
mod config;
|
||||
pub(crate) mod flags;
|
||||
pub mod flags;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ use crate::core::build_steps::clippy::get_clippy_rules_in_order;
|
|||
use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig};
|
||||
|
||||
fn parse(config: &str) -> Config {
|
||||
Config::parse_inner(&["check".to_string(), "--config=/does/not/exist".to_string()], |&_| {
|
||||
toml::from_str(&config).unwrap()
|
||||
})
|
||||
Config::parse_inner(
|
||||
Flags::parse(&["check".to_string(), "--config=/does/not/exist".to_string()]),
|
||||
|&_| toml::from_str(&config).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -108,7 +109,7 @@ fn clap_verify() {
|
|||
#[test]
|
||||
fn override_toml() {
|
||||
let config = Config::parse_inner(
|
||||
&[
|
||||
Flags::parse(&[
|
||||
"check".to_owned(),
|
||||
"--config=/does/not/exist".to_owned(),
|
||||
"--set=change-id=1".to_owned(),
|
||||
|
|
@ -121,7 +122,7 @@ fn override_toml() {
|
|||
"--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(),
|
||||
"--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(),
|
||||
"--set=target.aarch64-apple-darwin.runner=apple".to_owned(),
|
||||
],
|
||||
]),
|
||||
|&_| {
|
||||
toml::from_str(
|
||||
r#"
|
||||
|
|
@ -201,12 +202,12 @@ runner = "x86_64-runner"
|
|||
#[should_panic]
|
||||
fn override_toml_duplicate() {
|
||||
Config::parse_inner(
|
||||
&[
|
||||
Flags::parse(&[
|
||||
"check".to_owned(),
|
||||
"--config=/does/not/exist".to_string(),
|
||||
"--set=change-id=1".to_owned(),
|
||||
"--set=change-id=2".to_owned(),
|
||||
],
|
||||
]),
|
||||
|&_| toml::from_str("change-id = 0").unwrap(),
|
||||
);
|
||||
}
|
||||
|
|
@ -226,7 +227,7 @@ fn profile_user_dist() {
|
|||
.and_then(|table: toml::Value| TomlConfig::deserialize(table))
|
||||
.unwrap()
|
||||
}
|
||||
Config::parse_inner(&["check".to_owned()], get_toml);
|
||||
Config::parse_inner(Flags::parse(&["check".to_owned()]), get_toml);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -301,7 +302,7 @@ fn order_of_clippy_rules() {
|
|||
"-Aclippy::foo1".to_string(),
|
||||
"-Aclippy::foo2".to_string(),
|
||||
];
|
||||
let config = Config::parse(&args);
|
||||
let config = Config::parse(Flags::parse(&args));
|
||||
|
||||
let actual = match &config.cmd {
|
||||
crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => {
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ impl Config {
|
|||
let version = &self.stage0_metadata.compiler.version;
|
||||
let host = self.build;
|
||||
|
||||
let bin_root = self.out.join(host.triple).join("stage0");
|
||||
let bin_root = self.out.join(host).join("stage0");
|
||||
let clippy_stamp = bin_root.join(".clippy-stamp");
|
||||
let cargo_clippy = bin_root.join("bin").join(exe("cargo-clippy", host));
|
||||
if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, date) {
|
||||
|
|
@ -412,7 +412,7 @@ impl Config {
|
|||
let channel = format!("{version}-{date}");
|
||||
|
||||
let host = self.build;
|
||||
let bin_root = self.out.join(host.triple).join("rustfmt");
|
||||
let bin_root = self.out.join(host).join("rustfmt");
|
||||
let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host));
|
||||
let rustfmt_stamp = bin_root.join(".rustfmt-stamp");
|
||||
if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) {
|
||||
|
|
@ -592,7 +592,7 @@ impl Config {
|
|||
t!(fs::create_dir_all(&cache_dir));
|
||||
}
|
||||
|
||||
let bin_root = self.out.join(self.build.triple).join(destination);
|
||||
let bin_root = self.out.join(self.build).join(destination);
|
||||
let tarball = cache_dir.join(&filename);
|
||||
let (base_url, url, should_verify) = match mode {
|
||||
DownloadSource::CI => {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ mod core;
|
|||
mod utils;
|
||||
|
||||
pub use core::builder::PathSet;
|
||||
pub use core::config::flags::Subcommand;
|
||||
pub use core::config::flags::{Flags, Subcommand};
|
||||
pub use core::config::Config;
|
||||
|
||||
pub use utils::change_tracker::{
|
||||
|
|
@ -452,7 +452,7 @@ impl Build {
|
|||
}
|
||||
|
||||
// Make a symbolic link so we can use a consistent directory in the documentation.
|
||||
let build_triple = build.out.join(build.build.triple);
|
||||
let build_triple = build.out.join(build.build);
|
||||
t!(fs::create_dir_all(&build_triple));
|
||||
let host = build.out.join("host");
|
||||
if host.is_symlink() {
|
||||
|
|
@ -807,10 +807,7 @@ impl Build {
|
|||
}
|
||||
|
||||
fn tools_dir(&self, compiler: Compiler) -> PathBuf {
|
||||
let out = self
|
||||
.out
|
||||
.join(&*compiler.host.triple)
|
||||
.join(format!("stage{}-tools-bin", compiler.stage));
|
||||
let out = self.out.join(compiler.host).join(format!("stage{}-tools-bin", compiler.stage));
|
||||
t!(fs::create_dir_all(&out));
|
||||
out
|
||||
}
|
||||
|
|
@ -827,14 +824,14 @@ impl Build {
|
|||
Mode::ToolBootstrap => "-bootstrap-tools",
|
||||
Mode::ToolStd | Mode::ToolRustc => "-tools",
|
||||
};
|
||||
self.out.join(&*compiler.host.triple).join(format!("stage{}{}", compiler.stage, suffix))
|
||||
self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
|
||||
}
|
||||
|
||||
/// Returns the root output directory for all Cargo output in a given stage,
|
||||
/// running a particular compiler, whether or not we're building the
|
||||
/// standard library, and targeting the specified architecture.
|
||||
fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
|
||||
self.stage_out(compiler, mode).join(&*target.triple).join(self.cargo_dir())
|
||||
self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
|
||||
}
|
||||
|
||||
/// Root output directory of LLVM for `target`
|
||||
|
|
@ -845,36 +842,36 @@ impl Build {
|
|||
if self.config.llvm_from_ci && self.config.build == target {
|
||||
self.config.ci_llvm_root()
|
||||
} else {
|
||||
self.out.join(&*target.triple).join("llvm")
|
||||
self.out.join(target).join("llvm")
|
||||
}
|
||||
}
|
||||
|
||||
fn lld_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("lld")
|
||||
self.out.join(target).join("lld")
|
||||
}
|
||||
|
||||
/// Output directory for all documentation for a target
|
||||
fn doc_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("doc")
|
||||
self.out.join(target).join("doc")
|
||||
}
|
||||
|
||||
/// Output directory for all JSON-formatted documentation for a target
|
||||
fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("json-doc")
|
||||
self.out.join(target).join("json-doc")
|
||||
}
|
||||
|
||||
fn test_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("test")
|
||||
self.out.join(target).join("test")
|
||||
}
|
||||
|
||||
/// Output directory for all documentation for a target
|
||||
fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("compiler-doc")
|
||||
self.out.join(target).join("compiler-doc")
|
||||
}
|
||||
|
||||
/// Output directory for some generated md crate documentation for a target (temporary)
|
||||
fn md_doc_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("md-doc")
|
||||
self.out.join(target).join("md-doc")
|
||||
}
|
||||
|
||||
/// Returns `true` if this is an external version of LLVM not managed by bootstrap.
|
||||
|
|
@ -954,7 +951,7 @@ impl Build {
|
|||
|
||||
/// Directory for libraries built from C/C++ code and shared between stages.
|
||||
fn native_dir(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("native")
|
||||
self.out.join(target).join("native")
|
||||
}
|
||||
|
||||
/// Root output directory for rust_test_helpers library compiled for
|
||||
|
|
|
|||
|
|
@ -225,4 +225,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
|||
severity: ChangeSeverity::Info,
|
||||
summary: "New option `llvm.libzstd` to control whether llvm is built with zstd support.",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 128841,
|
||||
severity: ChangeSeverity::Warning,
|
||||
summary: "./x test --rustc-args was renamed to --compiletest-rustc-args as it only applies there. ./x miri --rustc-args was also removed.",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::path::PathBuf;
|
|||
use crate::utils::helpers::{
|
||||
check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, symlink_dir,
|
||||
};
|
||||
use crate::Config;
|
||||
use crate::{Config, Flags};
|
||||
|
||||
#[test]
|
||||
fn test_make() {
|
||||
|
|
@ -58,7 +58,8 @@ fn test_check_cfg_arg() {
|
|||
|
||||
#[test]
|
||||
fn test_program_out_of_date() {
|
||||
let config = Config::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]);
|
||||
let config =
|
||||
Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]));
|
||||
let tempfile = config.tempdir().join(".tmp-stamp-file");
|
||||
File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap();
|
||||
assert!(tempfile.exists());
|
||||
|
|
@ -73,7 +74,8 @@ fn test_program_out_of_date() {
|
|||
|
||||
#[test]
|
||||
fn test_symlink_dir() {
|
||||
let config = Config::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]);
|
||||
let config =
|
||||
Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]));
|
||||
let tempdir = config.tempdir().join(".tmp-dir");
|
||||
let link_path = config.tempdir().join(".tmp-link");
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ if [[ -z "${PR_CI_JOB}" ]]; then
|
|||
# compiler, and is sensitive to the addition of new flags.
|
||||
../x.py --stage 1 test tests/ui-fulldeps
|
||||
|
||||
# The tests are run a second time with the size optimizations enabled.
|
||||
../x.py --stage 1 test library/std library/alloc library/core \
|
||||
--rustc-args "--cfg feature=\"optimize_for_size\""
|
||||
# Rebuild the stdlib with the size optimizations enabled and run tests again.
|
||||
RUSTFLAGS_NOT_BOOTSTRAP="--cfg feature=\"optimize_for_size\"" ../x.py --stage 1 test \
|
||||
library/std library/alloc library/core
|
||||
fi
|
||||
|
||||
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
|
||||
|
|
|
|||
|
|
@ -376,6 +376,57 @@ that the code sample should be compiled using the respective edition of Rust.
|
|||
# fn foo() {}
|
||||
```
|
||||
|
||||
Starting in the 2024 edition[^edition-note], compatible doctests are merged as one before being
|
||||
run. We combine doctests for performance reasons: the slowest part of doctests is to compile them.
|
||||
Merging all of them into one file and compiling this new file, then running the doctests is much
|
||||
faster. Whether doctests are merged or not, they are run in their own process.
|
||||
|
||||
An example of time spent when running doctests:
|
||||
|
||||
[sysinfo crate](https://crates.io/crates/sysinfo):
|
||||
|
||||
```text
|
||||
wall-time duration: 4.59s
|
||||
total compile time: 27.067s
|
||||
total runtime: 3.969s
|
||||
```
|
||||
|
||||
Rust core library:
|
||||
|
||||
```text
|
||||
wall-time duration: 102s
|
||||
total compile time: 775.204s
|
||||
total runtime: 15.487s
|
||||
```
|
||||
|
||||
[^edition-note]: This is based on the edition of the whole crate, not the edition of the individual
|
||||
test case that may be specified in its code attribute.
|
||||
|
||||
In some cases, doctests cannot be merged. For example, if you have:
|
||||
|
||||
```rust
|
||||
//! ```
|
||||
//! let location = std::panic::Location::caller();
|
||||
//! assert_eq!(location.line(), 4);
|
||||
//! ```
|
||||
```
|
||||
|
||||
The problem with this code is that, if you change any other doctests, it'll likely break when
|
||||
runing `rustdoc --test`, making it tricky to maintain.
|
||||
|
||||
This is where the `standalone` attribute comes in: it tells `rustdoc` that a doctest
|
||||
should not be merged with the others. So the previous code should use it:
|
||||
|
||||
```rust
|
||||
//! ```standalone
|
||||
//! let location = std::panic::Location::caller();
|
||||
//! assert_eq!(location.line(), 4);
|
||||
//! ```
|
||||
```
|
||||
|
||||
In this case, it means that the line information will not change if you add/remove other
|
||||
doctests.
|
||||
|
||||
### Custom CSS classes for code blocks
|
||||
|
||||
```rust
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# `asm_const`
|
||||
|
||||
The tracking issue for this feature is: [#93332]
|
||||
|
||||
[#93332]: https://github.com/rust-lang/rust/issues/93332
|
||||
|
||||
------------------------
|
||||
|
||||
This feature adds a `const <expr>` operand type to `asm!` and `global_asm!`.
|
||||
- `<expr>` must be an integer constant expression.
|
||||
- The value of the expression is formatted as a string and substituted directly into the asm template string.
|
||||
|
|
@ -266,7 +266,7 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l enable-bolt-settings -d
|
|||
complete -c x.py -n "__fish_seen_subcommand_from doc" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
|
||||
complete -c x.py -n "__fish_seen_subcommand_from doc" -s h -l help -d 'Print help (see more with \'--help\')'
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l rustc-args -d 'extra options to pass the compiler when running tests' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r
|
||||
|
|
@ -313,7 +313,6 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l enable-bolt-settings -
|
|||
complete -c x.py -n "__fish_seen_subcommand_from test" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
|
||||
complete -c x.py -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help (see more with \'--help\')'
|
||||
complete -c x.py -n "__fish_seen_subcommand_from miri" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-args -d 'extra options to pass the compiler when running tests' -r
|
||||
complete -c x.py -n "__fish_seen_subcommand_from miri" -l config -d 'TOML configuration file for build' -r -F
|
||||
complete -c x.py -n "__fish_seen_subcommand_from miri" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
|
||||
complete -c x.py -n "__fish_seen_subcommand_from miri" -l build -d 'build target of the stage0 compiler' -r -f
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
|
|||
}
|
||||
'x.py;test' {
|
||||
[CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
|
||||
[CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests')
|
||||
[CompletionResult]::new('--compiletest-rustc-args', 'compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests')
|
||||
[CompletionResult]::new('--extra-checks', 'extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)')
|
||||
[CompletionResult]::new('--compare-mode', 'compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to')
|
||||
[CompletionResult]::new('--pass', 'pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode')
|
||||
|
|
@ -392,7 +392,6 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
|
|||
}
|
||||
'x.py;miri' {
|
||||
[CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
|
||||
[CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests')
|
||||
[CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build')
|
||||
[CompletionResult]::new('--build-dir', 'build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`')
|
||||
[CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler')
|
||||
|
|
|
|||
|
|
@ -1300,7 +1300,7 @@ _x.py() {
|
|||
return 0
|
||||
;;
|
||||
x.py__miri)
|
||||
opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
|
||||
opts="-v -i -j -h --no-fail-fast --test-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -1310,10 +1310,6 @@ _x.py() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--rustc-args)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--config)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
|
|
@ -1862,7 +1858,7 @@ _x.py() {
|
|||
return 0
|
||||
;;
|
||||
x.py__test)
|
||||
opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
|
||||
opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -1872,7 +1868,7 @@ _x.py() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--rustc-args)
|
||||
--compiletest-rustc-args)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ _arguments "${_arguments_options[@]}" \
|
|||
(test)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \
|
||||
'*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \
|
||||
'*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS: ' \
|
||||
'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS: ' \
|
||||
'--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE: ' \
|
||||
'--pass=[force {check,build,run}-pass tests to this mode]:check | build | run: ' \
|
||||
|
|
@ -393,7 +393,6 @@ _arguments "${_arguments_options[@]}" \
|
|||
(miri)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \
|
||||
'*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \
|
||||
'--config=[TOML configuration file for build]:FILE:_files' \
|
||||
'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
|
||||
'--build=[build target of the stage0 compiler]:BUILD:( )' \
|
||||
|
|
|
|||
|
|
@ -2069,7 +2069,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
|||
Some(ContainerTy::Ref(r)),
|
||||
)),
|
||||
},
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
// FIXME: should we merge the outer and inner binders somehow?
|
||||
let sig = bound_ty.skip_binder().fn_sig(cx.tcx);
|
||||
let decl = clean_poly_fn_sig(cx, None, sig);
|
||||
|
|
|
|||
|
|
@ -733,9 +733,11 @@ impl Options {
|
|||
let html_no_source = matches.opt_present("html-no-source");
|
||||
|
||||
if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
|
||||
dcx.fatal(
|
||||
"--generate-link-to-definition option can only be used with HTML output format",
|
||||
);
|
||||
dcx.struct_warn(
|
||||
"`--generate-link-to-definition` option can only be used with HTML output format",
|
||||
)
|
||||
.with_note("`--generate-link-to-definition` option will be ignored")
|
||||
.emit();
|
||||
}
|
||||
|
||||
let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
mod make;
|
||||
mod markdown;
|
||||
mod runner;
|
||||
mod rust;
|
||||
|
||||
use std::fs::File;
|
||||
|
|
@ -10,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::{panic, str};
|
||||
|
||||
pub(crate) use make::make_test;
|
||||
pub(crate) use make::DocTestBuilder;
|
||||
pub(crate) use markdown::test as test_markdown;
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
|
|
@ -150,8 +151,6 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<()
|
|||
expanded_args: options.expanded_args.clone(),
|
||||
};
|
||||
|
||||
let test_args = options.test_args.clone();
|
||||
let nocapture = options.nocapture;
|
||||
let externs = options.externs.clone();
|
||||
let json_unused_externs = options.json_unused_externs;
|
||||
|
||||
|
|
@ -164,39 +163,46 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<()
|
|||
let args_path = temp_dir.path().join("rustdoc-cfgs");
|
||||
crate::wrap_return(dcx, generate_args_file(&args_path, &options))?;
|
||||
|
||||
let (tests, unused_extern_reports, compiling_test_count) =
|
||||
interface::run_compiler(config, |compiler| {
|
||||
compiler.enter(|queries| {
|
||||
let collector = queries.global_ctxt()?.enter(|tcx| {
|
||||
let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
|
||||
let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
|
||||
let opts = scrape_test_config(crate_name, crate_attrs, args_path);
|
||||
let enable_per_target_ignores = options.enable_per_target_ignores;
|
||||
let CreateRunnableDocTests {
|
||||
standalone_tests,
|
||||
mergeable_tests,
|
||||
rustdoc_options,
|
||||
opts,
|
||||
unused_extern_reports,
|
||||
compiling_test_count,
|
||||
..
|
||||
} = interface::run_compiler(config, |compiler| {
|
||||
compiler.enter(|queries| {
|
||||
let collector = queries.global_ctxt()?.enter(|tcx| {
|
||||
let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
|
||||
let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
|
||||
let opts = scrape_test_config(crate_name, crate_attrs, args_path);
|
||||
let enable_per_target_ignores = options.enable_per_target_ignores;
|
||||
|
||||
let mut collector = CreateRunnableDoctests::new(options, opts);
|
||||
let hir_collector = HirCollector::new(
|
||||
&compiler.sess,
|
||||
tcx.hir(),
|
||||
ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
|
||||
enable_per_target_ignores,
|
||||
tcx,
|
||||
);
|
||||
let tests = hir_collector.collect_crate();
|
||||
tests.into_iter().for_each(|t| collector.add_test(t));
|
||||
let mut collector = CreateRunnableDocTests::new(options, opts);
|
||||
let hir_collector = HirCollector::new(
|
||||
&compiler.sess,
|
||||
tcx.hir(),
|
||||
ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
|
||||
enable_per_target_ignores,
|
||||
tcx,
|
||||
);
|
||||
let tests = hir_collector.collect_crate();
|
||||
tests.into_iter().for_each(|t| collector.add_test(t));
|
||||
|
||||
collector
|
||||
});
|
||||
if compiler.sess.dcx().has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
}
|
||||
collector
|
||||
});
|
||||
if compiler.sess.dcx().has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
}
|
||||
|
||||
let unused_extern_reports = collector.unused_extern_reports.clone();
|
||||
let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst);
|
||||
Ok((collector.tests, unused_extern_reports, compiling_test_count))
|
||||
})
|
||||
})?;
|
||||
Ok(collector)
|
||||
})
|
||||
})?;
|
||||
|
||||
run_tests(test_args, nocapture, tests);
|
||||
run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests);
|
||||
|
||||
let compiling_test_count = compiling_test_count.load(Ordering::SeqCst);
|
||||
|
||||
// Collect and warn about unused externs, but only if we've gotten
|
||||
// reports for each doctest
|
||||
|
|
@ -240,16 +246,83 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<()
|
|||
}
|
||||
|
||||
pub(crate) fn run_tests(
|
||||
mut test_args: Vec<String>,
|
||||
nocapture: bool,
|
||||
mut tests: Vec<test::TestDescAndFn>,
|
||||
opts: GlobalTestOptions,
|
||||
rustdoc_options: &Arc<RustdocOptions>,
|
||||
unused_extern_reports: &Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
mut standalone_tests: Vec<test::TestDescAndFn>,
|
||||
mergeable_tests: FxHashMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
) {
|
||||
let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1);
|
||||
test_args.insert(0, "rustdoctest".to_string());
|
||||
if nocapture {
|
||||
test_args.extend_from_slice(&rustdoc_options.test_args);
|
||||
if rustdoc_options.nocapture {
|
||||
test_args.push("--nocapture".to_string());
|
||||
}
|
||||
tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
|
||||
test::test_main(&test_args, tests, None);
|
||||
|
||||
let mut nb_errors = 0;
|
||||
let mut ran_edition_tests = 0;
|
||||
let target_str = rustdoc_options.target.to_string();
|
||||
|
||||
for (edition, mut doctests) in mergeable_tests {
|
||||
if doctests.is_empty() {
|
||||
continue;
|
||||
}
|
||||
doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name));
|
||||
|
||||
let mut tests_runner = runner::DocTestRunner::new();
|
||||
|
||||
let rustdoc_test_options = IndividualTestOptions::new(
|
||||
&rustdoc_options,
|
||||
&Some(format!("merged_doctest_{edition}")),
|
||||
PathBuf::from(format!("doctest_{edition}.rs")),
|
||||
);
|
||||
|
||||
for (doctest, scraped_test) in &doctests {
|
||||
tests_runner.add_test(doctest, scraped_test, &target_str);
|
||||
}
|
||||
if let Ok(success) = tests_runner.run_merged_tests(
|
||||
rustdoc_test_options,
|
||||
edition,
|
||||
&opts,
|
||||
&test_args,
|
||||
rustdoc_options,
|
||||
) {
|
||||
ran_edition_tests += 1;
|
||||
if !success {
|
||||
nb_errors += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// We failed to compile all compatible tests as one so we push them into the
|
||||
// `standalone_tests` doctests.
|
||||
debug!("Failed to compile compatible doctests for edition {} all at once", edition);
|
||||
for (doctest, scraped_test) in doctests {
|
||||
doctest.generate_unique_doctest(
|
||||
&scraped_test.text,
|
||||
scraped_test.langstr.test_harness,
|
||||
&opts,
|
||||
Some(&opts.crate_name),
|
||||
);
|
||||
standalone_tests.push(generate_test_desc_and_fn(
|
||||
doctest,
|
||||
scraped_test,
|
||||
opts.clone(),
|
||||
Arc::clone(&rustdoc_options),
|
||||
unused_extern_reports.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// We need to call `test_main` even if there is no doctest to run to get the output
|
||||
// `running 0 tests...`.
|
||||
if ran_edition_tests == 0 || !standalone_tests.is_empty() {
|
||||
standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
|
||||
test::test_main(&test_args, standalone_tests, None);
|
||||
}
|
||||
if nb_errors != 0 {
|
||||
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
|
||||
std::process::exit(101);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
|
||||
|
|
@ -330,7 +403,7 @@ impl DirState {
|
|||
// We could unify this struct the one in rustc but they have different
|
||||
// ownership semantics, so doing so would create wasteful allocations.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct UnusedExterns {
|
||||
pub(crate) struct UnusedExterns {
|
||||
/// Lint level of the unused_crate_dependencies lint
|
||||
lint_level: String,
|
||||
/// List of unused externs by their names.
|
||||
|
|
@ -359,22 +432,41 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com
|
|||
command
|
||||
}
|
||||
|
||||
struct RunnableDoctest {
|
||||
/// Information needed for running a bundle of doctests.
|
||||
///
|
||||
/// This data structure contains the "full" test code, including the wrappers
|
||||
/// (if multiple doctests are merged), `main` function,
|
||||
/// and everything needed to calculate the compiler's command-line arguments.
|
||||
/// The `# ` prefix on boring lines has also been stripped.
|
||||
pub(crate) struct RunnableDocTest {
|
||||
full_test_code: String,
|
||||
full_test_line_offset: usize,
|
||||
test_opts: IndividualTestOptions,
|
||||
global_opts: GlobalTestOptions,
|
||||
scraped_test: ScrapedDoctest,
|
||||
langstr: LangString,
|
||||
line: usize,
|
||||
edition: Edition,
|
||||
no_run: bool,
|
||||
is_multiple_tests: bool,
|
||||
}
|
||||
|
||||
impl RunnableDocTest {
|
||||
fn path_for_merged_doctest(&self) -> PathBuf {
|
||||
self.test_opts.outdir.path().join(&format!("doctest_{}.rs", self.edition))
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a `RunnableDoctest`.
|
||||
///
|
||||
/// This is the function that calculates the compiler command line, invokes the compiler, then
|
||||
/// invokes the test or tests in a separate executable (if applicable).
|
||||
fn run_test(
|
||||
doctest: RunnableDoctest,
|
||||
doctest: RunnableDocTest,
|
||||
rustdoc_options: &RustdocOptions,
|
||||
supports_color: bool,
|
||||
report_unused_externs: impl Fn(UnusedExterns),
|
||||
) -> Result<(), TestFailure> {
|
||||
let scraped_test = &doctest.scraped_test;
|
||||
let langstr = &scraped_test.langstr;
|
||||
let langstr = &doctest.langstr;
|
||||
// Make sure we emit well-formed executable names for our target.
|
||||
let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target);
|
||||
let output_file = doctest.test_opts.outdir.path().join(rust_out);
|
||||
|
|
@ -391,12 +483,15 @@ fn run_test(
|
|||
compiler.arg(format!("--sysroot={}", sysroot.display()));
|
||||
}
|
||||
|
||||
compiler.arg("--edition").arg(&scraped_test.edition(rustdoc_options).to_string());
|
||||
compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
|
||||
compiler.env(
|
||||
"UNSTABLE_RUSTDOC_TEST_LINE",
|
||||
format!("{}", scraped_test.line as isize - doctest.full_test_line_offset as isize),
|
||||
);
|
||||
compiler.arg("--edition").arg(&doctest.edition.to_string());
|
||||
if !doctest.is_multiple_tests {
|
||||
// Setting these environment variables is unneeded if this is a merged doctest.
|
||||
compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
|
||||
compiler.env(
|
||||
"UNSTABLE_RUSTDOC_TEST_LINE",
|
||||
format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize),
|
||||
);
|
||||
}
|
||||
compiler.arg("-o").arg(&output_file);
|
||||
if langstr.test_harness {
|
||||
compiler.arg("--test");
|
||||
|
|
@ -408,10 +503,7 @@ fn run_test(
|
|||
compiler.arg("-Z").arg("unstable-options");
|
||||
}
|
||||
|
||||
if scraped_test.no_run(rustdoc_options)
|
||||
&& !langstr.compile_fail
|
||||
&& rustdoc_options.persist_doctests.is_none()
|
||||
{
|
||||
if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() {
|
||||
// FIXME: why does this code check if it *shouldn't* persist doctests
|
||||
// -- shouldn't it be the negation?
|
||||
compiler.arg("--emit=metadata");
|
||||
|
|
@ -442,18 +534,40 @@ fn run_test(
|
|||
}
|
||||
}
|
||||
|
||||
compiler.arg("-");
|
||||
compiler.stdin(Stdio::piped());
|
||||
compiler.stderr(Stdio::piped());
|
||||
// If this is a merged doctest, we need to write it into a file instead of using stdin
|
||||
// because if the size of the merged doctests is too big, it'll simply break stdin.
|
||||
if doctest.is_multiple_tests {
|
||||
// It makes the compilation failure much faster if it is for a combined doctest.
|
||||
compiler.arg("--error-format=short");
|
||||
let input_file = doctest.path_for_merged_doctest();
|
||||
if std::fs::write(&input_file, &doctest.full_test_code).is_err() {
|
||||
// If we cannot write this file for any reason, we leave. All combined tests will be
|
||||
// tested as standalone tests.
|
||||
return Err(TestFailure::CompileError);
|
||||
}
|
||||
compiler.arg(input_file);
|
||||
if !rustdoc_options.nocapture {
|
||||
// If `nocapture` is disabled, then we don't display rustc's output when compiling
|
||||
// the merged doctests.
|
||||
compiler.stderr(Stdio::null());
|
||||
}
|
||||
} else {
|
||||
compiler.arg("-");
|
||||
compiler.stdin(Stdio::piped());
|
||||
compiler.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
debug!("compiler invocation for doctest: {compiler:?}");
|
||||
|
||||
let mut child = compiler.spawn().expect("Failed to spawn rustc process");
|
||||
{
|
||||
let output = if doctest.is_multiple_tests {
|
||||
let status = child.wait().expect("Failed to wait");
|
||||
process::Output { status, stdout: Vec::new(), stderr: Vec::new() }
|
||||
} else {
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");
|
||||
}
|
||||
let output = child.wait_with_output().expect("Failed to read stdout");
|
||||
child.wait_with_output().expect("Failed to read stdout")
|
||||
};
|
||||
|
||||
struct Bomb<'a>(&'a str);
|
||||
impl Drop for Bomb<'_> {
|
||||
|
|
@ -492,8 +606,7 @@ fn run_test(
|
|||
// We used to check if the output contained "error[{}]: " but since we added the
|
||||
// colored output, we can't anymore because of the color escape characters before
|
||||
// the ":".
|
||||
let missing_codes: Vec<String> = scraped_test
|
||||
.langstr
|
||||
let missing_codes: Vec<String> = langstr
|
||||
.error_codes
|
||||
.iter()
|
||||
.filter(|err| !out.contains(&format!("error[{err}]")))
|
||||
|
|
@ -510,7 +623,7 @@ fn run_test(
|
|||
}
|
||||
}
|
||||
|
||||
if scraped_test.no_run(rustdoc_options) {
|
||||
if doctest.no_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -522,15 +635,19 @@ fn run_test(
|
|||
let tool = make_maybe_absolute_path(tool.into());
|
||||
cmd = Command::new(tool);
|
||||
cmd.args(&rustdoc_options.runtool_args);
|
||||
cmd.arg(output_file);
|
||||
cmd.arg(&output_file);
|
||||
} else {
|
||||
cmd = Command::new(output_file);
|
||||
cmd = Command::new(&output_file);
|
||||
if doctest.is_multiple_tests {
|
||||
cmd.arg("*doctest-bin-path");
|
||||
cmd.arg(&output_file);
|
||||
}
|
||||
}
|
||||
if let Some(run_directory) = &rustdoc_options.test_run_directory {
|
||||
cmd.current_dir(run_directory);
|
||||
}
|
||||
|
||||
let result = if rustdoc_options.nocapture {
|
||||
let result = if doctest.is_multiple_tests || rustdoc_options.nocapture {
|
||||
cmd.status().map(|status| process::Output {
|
||||
status,
|
||||
stdout: Vec::new(),
|
||||
|
|
@ -568,15 +685,14 @@ fn make_maybe_absolute_path(path: PathBuf) -> PathBuf {
|
|||
}
|
||||
struct IndividualTestOptions {
|
||||
outdir: DirState,
|
||||
test_id: String,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl IndividualTestOptions {
|
||||
fn new(options: &RustdocOptions, test_id: String, test_path: PathBuf) -> Self {
|
||||
fn new(options: &RustdocOptions, test_id: &Option<String>, test_path: PathBuf) -> Self {
|
||||
let outdir = if let Some(ref path) = options.persist_doctests {
|
||||
let mut path = path.clone();
|
||||
path.push(&test_id);
|
||||
path.push(&test_id.as_deref().unwrap_or_else(|| "<doctest>"));
|
||||
|
||||
if let Err(err) = std::fs::create_dir_all(&path) {
|
||||
eprintln!("Couldn't create directory for doctest executables: {err}");
|
||||
|
|
@ -588,20 +704,45 @@ impl IndividualTestOptions {
|
|||
DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir"))
|
||||
};
|
||||
|
||||
Self { outdir, test_id, path: test_path }
|
||||
Self { outdir, path: test_path }
|
||||
}
|
||||
}
|
||||
|
||||
/// A doctest scraped from the code, ready to be turned into a runnable test.
|
||||
struct ScrapedDoctest {
|
||||
///
|
||||
/// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`.
|
||||
/// [`run_merged_tests`] converts a bunch of scraped doctests to a single runnable doctest,
|
||||
/// while [`generate_unique_doctest`] does the standalones.
|
||||
///
|
||||
/// [`clean`]: crate::clean
|
||||
/// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests
|
||||
/// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest
|
||||
pub(crate) struct ScrapedDocTest {
|
||||
filename: FileName,
|
||||
line: usize,
|
||||
logical_path: Vec<String>,
|
||||
langstr: LangString,
|
||||
text: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ScrapedDoctest {
|
||||
impl ScrapedDocTest {
|
||||
fn new(
|
||||
filename: FileName,
|
||||
line: usize,
|
||||
logical_path: Vec<String>,
|
||||
langstr: LangString,
|
||||
text: String,
|
||||
) -> Self {
|
||||
let mut item_path = logical_path.join("::");
|
||||
item_path.retain(|c| c != ' ');
|
||||
if !item_path.is_empty() {
|
||||
item_path.push(' ');
|
||||
}
|
||||
let name =
|
||||
format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly());
|
||||
|
||||
Self { filename, line, langstr, text, name }
|
||||
}
|
||||
fn edition(&self, opts: &RustdocOptions) -> Edition {
|
||||
self.langstr.edition.unwrap_or(opts.edition)
|
||||
}
|
||||
|
|
@ -609,54 +750,8 @@ impl ScrapedDoctest {
|
|||
fn no_run(&self, opts: &RustdocOptions) -> bool {
|
||||
self.langstr.no_run || opts.no_run
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait DoctestVisitor {
|
||||
fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine);
|
||||
fn visit_header(&mut self, _name: &str, _level: u32) {}
|
||||
}
|
||||
|
||||
struct CreateRunnableDoctests {
|
||||
tests: Vec<test::TestDescAndFn>,
|
||||
|
||||
rustdoc_options: Arc<RustdocOptions>,
|
||||
opts: GlobalTestOptions,
|
||||
visited_tests: FxHashMap<(String, usize), usize>,
|
||||
unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
compiling_test_count: AtomicUsize,
|
||||
}
|
||||
|
||||
impl CreateRunnableDoctests {
|
||||
fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests {
|
||||
CreateRunnableDoctests {
|
||||
tests: Vec::new(),
|
||||
rustdoc_options: Arc::new(rustdoc_options),
|
||||
opts,
|
||||
visited_tests: FxHashMap::default(),
|
||||
unused_extern_reports: Default::default(),
|
||||
compiling_test_count: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_name(&self, filename: &FileName, line: usize, logical_path: &[String]) -> String {
|
||||
let mut item_path = logical_path.join("::");
|
||||
item_path.retain(|c| c != ' ');
|
||||
if !item_path.is_empty() {
|
||||
item_path.push(' ');
|
||||
}
|
||||
format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly())
|
||||
}
|
||||
|
||||
fn add_test(&mut self, test: ScrapedDoctest) {
|
||||
let name = self.generate_name(&test.filename, test.line, &test.logical_path);
|
||||
let opts = self.opts.clone();
|
||||
let target_str = self.rustdoc_options.target.to_string();
|
||||
let unused_externs = self.unused_extern_reports.clone();
|
||||
if !test.langstr.compile_fail {
|
||||
self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let path = match &test.filename {
|
||||
fn path(&self) -> PathBuf {
|
||||
match &self.filename {
|
||||
FileName::Real(path) => {
|
||||
if let Some(local_path) = path.local_path() {
|
||||
local_path.to_path_buf()
|
||||
|
|
@ -666,10 +761,45 @@ impl CreateRunnableDoctests {
|
|||
}
|
||||
}
|
||||
_ => PathBuf::from(r"doctest.rs"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait DocTestVisitor {
|
||||
fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine);
|
||||
fn visit_header(&mut self, _name: &str, _level: u32) {}
|
||||
}
|
||||
|
||||
struct CreateRunnableDocTests {
|
||||
standalone_tests: Vec<test::TestDescAndFn>,
|
||||
mergeable_tests: FxHashMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
|
||||
rustdoc_options: Arc<RustdocOptions>,
|
||||
opts: GlobalTestOptions,
|
||||
visited_tests: FxHashMap<(String, usize), usize>,
|
||||
unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
compiling_test_count: AtomicUsize,
|
||||
can_merge_doctests: bool,
|
||||
}
|
||||
|
||||
impl CreateRunnableDocTests {
|
||||
fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDocTests {
|
||||
let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024;
|
||||
CreateRunnableDocTests {
|
||||
standalone_tests: Vec::new(),
|
||||
mergeable_tests: FxHashMap::default(),
|
||||
rustdoc_options: Arc::new(rustdoc_options),
|
||||
opts,
|
||||
visited_tests: FxHashMap::default(),
|
||||
unused_extern_reports: Default::default(),
|
||||
compiling_test_count: AtomicUsize::new(0),
|
||||
can_merge_doctests,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_test(&mut self, scraped_test: ScrapedDocTest) {
|
||||
// For example `module/file.rs` would become `module_file_rs`
|
||||
let file = test
|
||||
let file = scraped_test
|
||||
.filename
|
||||
.prefer_local()
|
||||
.to_string_lossy()
|
||||
|
|
@ -679,75 +809,134 @@ impl CreateRunnableDoctests {
|
|||
let test_id = format!(
|
||||
"{file}_{line}_{number}",
|
||||
file = file,
|
||||
line = test.line,
|
||||
line = scraped_test.line,
|
||||
number = {
|
||||
// Increases the current test number, if this file already
|
||||
// exists or it creates a new entry with a test number of 0.
|
||||
self.visited_tests
|
||||
.entry((file.clone(), test.line))
|
||||
.entry((file.clone(), scraped_test.line))
|
||||
.and_modify(|v| *v += 1)
|
||||
.or_insert(0)
|
||||
},
|
||||
);
|
||||
|
||||
let rustdoc_options = self.rustdoc_options.clone();
|
||||
let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path);
|
||||
let edition = scraped_test.edition(&self.rustdoc_options);
|
||||
let doctest = DocTestBuilder::new(
|
||||
&scraped_test.text,
|
||||
Some(&self.opts.crate_name),
|
||||
edition,
|
||||
self.can_merge_doctests,
|
||||
Some(test_id),
|
||||
Some(&scraped_test.langstr),
|
||||
);
|
||||
let is_standalone = !doctest.can_be_merged
|
||||
|| scraped_test.langstr.compile_fail
|
||||
|| scraped_test.langstr.test_harness
|
||||
|| scraped_test.langstr.standalone
|
||||
|| self.rustdoc_options.nocapture
|
||||
|| self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output");
|
||||
if is_standalone {
|
||||
let test_desc = self.generate_test_desc_and_fn(doctest, scraped_test);
|
||||
self.standalone_tests.push(test_desc);
|
||||
} else {
|
||||
self.mergeable_tests.entry(edition).or_default().push((doctest, scraped_test));
|
||||
}
|
||||
}
|
||||
|
||||
debug!("creating test {name}: {}", test.text);
|
||||
self.tests.push(test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: test::DynTestName(name),
|
||||
ignore: match test.langstr.ignore {
|
||||
Ignore::All => true,
|
||||
Ignore::None => false,
|
||||
Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
|
||||
},
|
||||
ignore_message: None,
|
||||
source_file: "",
|
||||
start_line: 0,
|
||||
start_col: 0,
|
||||
end_line: 0,
|
||||
end_col: 0,
|
||||
// compiler failures are test failures
|
||||
should_panic: test::ShouldPanic::No,
|
||||
compile_fail: test.langstr.compile_fail,
|
||||
no_run: test.no_run(&rustdoc_options),
|
||||
test_type: test::TestType::DocTest,
|
||||
fn generate_test_desc_and_fn(
|
||||
&mut self,
|
||||
test: DocTestBuilder,
|
||||
scraped_test: ScrapedDocTest,
|
||||
) -> test::TestDescAndFn {
|
||||
if !scraped_test.langstr.compile_fail {
|
||||
self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
generate_test_desc_and_fn(
|
||||
test,
|
||||
scraped_test,
|
||||
self.opts.clone(),
|
||||
Arc::clone(&self.rustdoc_options),
|
||||
self.unused_extern_reports.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_test_desc_and_fn(
|
||||
test: DocTestBuilder,
|
||||
scraped_test: ScrapedDocTest,
|
||||
opts: GlobalTestOptions,
|
||||
rustdoc_options: Arc<RustdocOptions>,
|
||||
unused_externs: Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
) -> test::TestDescAndFn {
|
||||
let target_str = rustdoc_options.target.to_string();
|
||||
let rustdoc_test_options =
|
||||
IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.path());
|
||||
|
||||
debug!("creating test {}: {}", scraped_test.name, scraped_test.text);
|
||||
test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: test::DynTestName(scraped_test.name.clone()),
|
||||
ignore: match scraped_test.langstr.ignore {
|
||||
Ignore::All => true,
|
||||
Ignore::None => false,
|
||||
Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
|
||||
},
|
||||
testfn: test::DynTestFn(Box::new(move || {
|
||||
doctest_run_fn(rustdoc_test_options, opts, test, rustdoc_options, unused_externs)
|
||||
})),
|
||||
});
|
||||
ignore_message: None,
|
||||
source_file: "",
|
||||
start_line: 0,
|
||||
start_col: 0,
|
||||
end_line: 0,
|
||||
end_col: 0,
|
||||
// compiler failures are test failures
|
||||
should_panic: test::ShouldPanic::No,
|
||||
compile_fail: scraped_test.langstr.compile_fail,
|
||||
no_run: scraped_test.no_run(&rustdoc_options),
|
||||
test_type: test::TestType::DocTest,
|
||||
},
|
||||
testfn: test::DynTestFn(Box::new(move || {
|
||||
doctest_run_fn(
|
||||
rustdoc_test_options,
|
||||
opts,
|
||||
test,
|
||||
scraped_test,
|
||||
rustdoc_options,
|
||||
unused_externs,
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn doctest_run_fn(
|
||||
test_opts: IndividualTestOptions,
|
||||
global_opts: GlobalTestOptions,
|
||||
scraped_test: ScrapedDoctest,
|
||||
doctest: DocTestBuilder,
|
||||
scraped_test: ScrapedDocTest,
|
||||
rustdoc_options: Arc<RustdocOptions>,
|
||||
unused_externs: Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
) -> Result<(), String> {
|
||||
let report_unused_externs = |uext| {
|
||||
unused_externs.lock().unwrap().push(uext);
|
||||
};
|
||||
let edition = scraped_test.edition(&rustdoc_options);
|
||||
let (full_test_code, full_test_line_offset, supports_color) = make_test(
|
||||
let (full_test_code, full_test_line_offset) = doctest.generate_unique_doctest(
|
||||
&scraped_test.text,
|
||||
Some(&global_opts.crate_name),
|
||||
scraped_test.langstr.test_harness,
|
||||
&global_opts,
|
||||
edition,
|
||||
Some(&test_opts.test_id),
|
||||
Some(&global_opts.crate_name),
|
||||
);
|
||||
let runnable_test = RunnableDoctest {
|
||||
let runnable_test = RunnableDocTest {
|
||||
full_test_code,
|
||||
full_test_line_offset,
|
||||
test_opts,
|
||||
global_opts,
|
||||
scraped_test,
|
||||
langstr: scraped_test.langstr.clone(),
|
||||
line: scraped_test.line,
|
||||
edition: scraped_test.edition(&rustdoc_options),
|
||||
no_run: scraped_test.no_run(&rustdoc_options),
|
||||
is_multiple_tests: false,
|
||||
};
|
||||
let res = run_test(runnable_test, &rustdoc_options, supports_color, report_unused_externs);
|
||||
let res =
|
||||
run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs);
|
||||
|
||||
if let Err(err) = res {
|
||||
match err {
|
||||
|
|
@ -804,7 +993,7 @@ fn doctest_run_fn(
|
|||
}
|
||||
|
||||
#[cfg(test)] // used in tests
|
||||
impl DoctestVisitor for Vec<usize> {
|
||||
impl DocTestVisitor for Vec<usize> {
|
||||
fn visit_test(&mut self, _test: String, _config: LangString, rel_line: MdRelLine) {
|
||||
self.push(1 + rel_line.offset());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,250 +16,428 @@ use rustc_span::symbol::sym;
|
|||
use rustc_span::FileName;
|
||||
|
||||
use super::GlobalTestOptions;
|
||||
use crate::html::markdown::LangString;
|
||||
|
||||
/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
|
||||
/// lines before the test code begins as well as if the output stream supports colors or not.
|
||||
pub(crate) fn make_test(
|
||||
s: &str,
|
||||
crate_name: Option<&str>,
|
||||
dont_insert_main: bool,
|
||||
opts: &GlobalTestOptions,
|
||||
edition: Edition,
|
||||
test_id: Option<&str>,
|
||||
) -> (String, usize, bool) {
|
||||
let (crate_attrs, everything_else, crates) = partition_source(s, edition);
|
||||
let everything_else = everything_else.trim();
|
||||
let mut line_offset = 0;
|
||||
let mut prog = String::new();
|
||||
let mut supports_color = false;
|
||||
/// This struct contains information about the doctest itself which is then used to generate
|
||||
/// doctest source code appropriately.
|
||||
pub(crate) struct DocTestBuilder {
|
||||
pub(crate) supports_color: bool,
|
||||
pub(crate) already_has_extern_crate: bool,
|
||||
pub(crate) has_main_fn: bool,
|
||||
pub(crate) crate_attrs: String,
|
||||
/// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
|
||||
/// put into `crate_attrs`.
|
||||
pub(crate) maybe_crate_attrs: String,
|
||||
pub(crate) crates: String,
|
||||
pub(crate) everything_else: String,
|
||||
pub(crate) test_id: Option<String>,
|
||||
pub(crate) failed_ast: bool,
|
||||
pub(crate) can_be_merged: bool,
|
||||
}
|
||||
|
||||
if opts.attrs.is_empty() {
|
||||
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
|
||||
// lints that are commonly triggered in doctests. The crate-level test attributes are
|
||||
// commonly used to make tests fail in case they trigger warnings, so having this there in
|
||||
// that case may cause some tests to pass when they shouldn't have.
|
||||
prog.push_str("#![allow(unused)]\n");
|
||||
line_offset += 1;
|
||||
impl DocTestBuilder {
|
||||
pub(crate) fn new(
|
||||
source: &str,
|
||||
crate_name: Option<&str>,
|
||||
edition: Edition,
|
||||
can_merge_doctests: bool,
|
||||
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
|
||||
test_id: Option<String>,
|
||||
lang_str: Option<&LangString>,
|
||||
) -> Self {
|
||||
let can_merge_doctests = can_merge_doctests
|
||||
&& lang_str.is_some_and(|lang_str| {
|
||||
!lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone
|
||||
});
|
||||
|
||||
let SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else } =
|
||||
partition_source(source, edition);
|
||||
|
||||
// Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
|
||||
// crate already is included.
|
||||
let Ok((
|
||||
ParseSourceInfo {
|
||||
has_main_fn,
|
||||
found_extern_crate,
|
||||
supports_color,
|
||||
has_global_allocator,
|
||||
has_macro_def,
|
||||
..
|
||||
},
|
||||
failed_ast,
|
||||
)) = check_for_main_and_extern_crate(
|
||||
crate_name,
|
||||
source,
|
||||
&everything_else,
|
||||
&crates,
|
||||
edition,
|
||||
can_merge_doctests,
|
||||
)
|
||||
else {
|
||||
// If the parser panicked due to a fatal error, pass the test code through unchanged.
|
||||
// The error will be reported during compilation.
|
||||
return Self {
|
||||
supports_color: false,
|
||||
has_main_fn: false,
|
||||
crate_attrs,
|
||||
maybe_crate_attrs,
|
||||
crates,
|
||||
everything_else,
|
||||
already_has_extern_crate: false,
|
||||
test_id,
|
||||
failed_ast: true,
|
||||
can_be_merged: false,
|
||||
};
|
||||
};
|
||||
// If the AST returned an error, we don't want this doctest to be merged with the
|
||||
// others. Same if it contains `#[feature]` or `#[no_std]`.
|
||||
let can_be_merged = can_merge_doctests
|
||||
&& !failed_ast
|
||||
&& !has_global_allocator
|
||||
&& crate_attrs.is_empty()
|
||||
// If this is a merged doctest and a defined macro uses `$crate`, then the path will
|
||||
// not work, so better not put it into merged doctests.
|
||||
&& !(has_macro_def && everything_else.contains("$crate"));
|
||||
Self {
|
||||
supports_color,
|
||||
has_main_fn,
|
||||
crate_attrs,
|
||||
maybe_crate_attrs,
|
||||
crates,
|
||||
everything_else,
|
||||
already_has_extern_crate: found_extern_crate,
|
||||
test_id,
|
||||
failed_ast: false,
|
||||
can_be_merged,
|
||||
}
|
||||
}
|
||||
|
||||
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
|
||||
for attr in &opts.attrs {
|
||||
prog.push_str(&format!("#![{attr}]\n"));
|
||||
line_offset += 1;
|
||||
}
|
||||
/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
|
||||
/// lines before the test code begins.
|
||||
pub(crate) fn generate_unique_doctest(
|
||||
&self,
|
||||
test_code: &str,
|
||||
dont_insert_main: bool,
|
||||
opts: &GlobalTestOptions,
|
||||
crate_name: Option<&str>,
|
||||
) -> (String, usize) {
|
||||
if self.failed_ast {
|
||||
// If the AST failed to compile, no need to go generate a complete doctest, the error
|
||||
// will be better this way.
|
||||
return (test_code.to_string(), 0);
|
||||
}
|
||||
let mut line_offset = 0;
|
||||
let mut prog = String::new();
|
||||
let everything_else = self.everything_else.trim();
|
||||
if opts.attrs.is_empty() {
|
||||
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
|
||||
// lints that are commonly triggered in doctests. The crate-level test attributes are
|
||||
// commonly used to make tests fail in case they trigger warnings, so having this there in
|
||||
// that case may cause some tests to pass when they shouldn't have.
|
||||
prog.push_str("#![allow(unused)]\n");
|
||||
line_offset += 1;
|
||||
}
|
||||
|
||||
// Now push any outer attributes from the example, assuming they
|
||||
// are intended to be crate attributes.
|
||||
prog.push_str(&crate_attrs);
|
||||
prog.push_str(&crates);
|
||||
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
|
||||
for attr in &opts.attrs {
|
||||
prog.push_str(&format!("#![{attr}]\n"));
|
||||
line_offset += 1;
|
||||
}
|
||||
|
||||
// Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
|
||||
// crate already is included.
|
||||
let Ok((already_has_main, already_has_extern_crate)) =
|
||||
check_for_main_and_extern_crate(crate_name, s.to_owned(), edition, &mut supports_color)
|
||||
else {
|
||||
// If the parser panicked due to a fatal error, pass the test code through unchanged.
|
||||
// The error will be reported during compilation.
|
||||
return (s.to_owned(), 0, false);
|
||||
};
|
||||
// Now push any outer attributes from the example, assuming they
|
||||
// are intended to be crate attributes.
|
||||
prog.push_str(&self.crate_attrs);
|
||||
prog.push_str(&self.maybe_crate_attrs);
|
||||
prog.push_str(&self.crates);
|
||||
|
||||
// Don't inject `extern crate std` because it's already injected by the
|
||||
// compiler.
|
||||
if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") {
|
||||
if let Some(crate_name) = crate_name {
|
||||
// Don't inject `extern crate std` because it's already injected by the
|
||||
// compiler.
|
||||
if !self.already_has_extern_crate &&
|
||||
!opts.no_crate_inject &&
|
||||
let Some(crate_name) = crate_name &&
|
||||
crate_name != "std" &&
|
||||
// Don't inject `extern crate` if the crate is never used.
|
||||
// NOTE: this is terribly inaccurate because it doesn't actually
|
||||
// parse the source, but only has false positives, not false
|
||||
// negatives.
|
||||
if s.contains(crate_name) {
|
||||
// rustdoc implicitly inserts an `extern crate` item for the own crate
|
||||
// which may be unused, so we need to allow the lint.
|
||||
prog.push_str("#[allow(unused_extern_crates)]\n");
|
||||
test_code.contains(crate_name)
|
||||
{
|
||||
// rustdoc implicitly inserts an `extern crate` item for the own crate
|
||||
// which may be unused, so we need to allow the lint.
|
||||
prog.push_str("#[allow(unused_extern_crates)]\n");
|
||||
|
||||
prog.push_str(&format!("extern crate r#{crate_name};\n"));
|
||||
line_offset += 1;
|
||||
prog.push_str(&format!("extern crate r#{crate_name};\n"));
|
||||
line_offset += 1;
|
||||
}
|
||||
|
||||
// FIXME: This code cannot yet handle no_std test cases yet
|
||||
if dont_insert_main || self.has_main_fn || prog.contains("![no_std]") {
|
||||
prog.push_str(everything_else);
|
||||
} else {
|
||||
let returns_result = everything_else.ends_with("(())");
|
||||
// Give each doctest main function a unique name.
|
||||
// This is for example needed for the tooling around `-C instrument-coverage`.
|
||||
let inner_fn_name = if let Some(ref test_id) = self.test_id {
|
||||
format!("_doctest_main_{test_id}")
|
||||
} else {
|
||||
"_inner".into()
|
||||
};
|
||||
let inner_attr = if self.test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
|
||||
let (main_pre, main_post) = if returns_result {
|
||||
(
|
||||
format!(
|
||||
"fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n",
|
||||
),
|
||||
format!("\n}} {inner_fn_name}().unwrap() }}"),
|
||||
)
|
||||
} else if self.test_id.is_some() {
|
||||
(
|
||||
format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",),
|
||||
format!("\n}} {inner_fn_name}() }}"),
|
||||
)
|
||||
} else {
|
||||
("fn main() {\n".into(), "\n}".into())
|
||||
};
|
||||
// Note on newlines: We insert a line/newline *before*, and *after*
|
||||
// the doctest and adjust the `line_offset` accordingly.
|
||||
// In the case of `-C instrument-coverage`, this means that the generated
|
||||
// inner `main` function spans from the doctest opening codeblock to the
|
||||
// closing one. For example
|
||||
// /// ``` <- start of the inner main
|
||||
// /// <- code under doctest
|
||||
// /// ``` <- end of the inner main
|
||||
line_offset += 1;
|
||||
|
||||
prog.push_str(&main_pre);
|
||||
|
||||
// add extra 4 spaces for each line to offset the code block
|
||||
if opts.insert_indent_space {
|
||||
prog.push_str(
|
||||
&everything_else
|
||||
.lines()
|
||||
.map(|line| format!(" {}", line))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n"),
|
||||
);
|
||||
} else {
|
||||
prog.push_str(everything_else);
|
||||
};
|
||||
prog.push_str(&main_post);
|
||||
}
|
||||
|
||||
debug!("final doctest:\n{prog}");
|
||||
|
||||
(prog, line_offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum ParsingResult {
|
||||
Failed,
|
||||
AstError,
|
||||
Ok,
|
||||
}
|
||||
|
||||
fn cancel_error_count(psess: &ParseSess) {
|
||||
// Reset errors so that they won't be reported as compiler bugs when dropping the
|
||||
// dcx. Any errors in the tests will be reported when the test file is compiled,
|
||||
// Note that we still need to cancel the errors above otherwise `Diag` will panic on
|
||||
// drop.
|
||||
psess.dcx().reset_err_count();
|
||||
}
|
||||
|
||||
fn parse_source(
|
||||
source: String,
|
||||
info: &mut ParseSourceInfo,
|
||||
crate_name: &Option<&str>,
|
||||
) -> ParsingResult {
|
||||
use rustc_errors::emitter::{Emitter, HumanEmitter};
|
||||
use rustc_errors::DiagCtxt;
|
||||
use rustc_parse::parser::ForceCollect;
|
||||
use rustc_span::source_map::FilePathMapping;
|
||||
|
||||
let filename = FileName::anon_source_code(&source);
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
false,
|
||||
);
|
||||
info.supports_color =
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
|
||||
.supports_color();
|
||||
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
let psess = ParseSess::with_dcx(dcx, sm);
|
||||
|
||||
let mut parser = match new_parser_from_source_str(&psess, filename, source) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
errs.into_iter().for_each(|err| err.cancel());
|
||||
cancel_error_count(&psess);
|
||||
return ParsingResult::Failed;
|
||||
}
|
||||
};
|
||||
let mut parsing_result = ParsingResult::Ok;
|
||||
|
||||
// Recurse through functions body. It is necessary because the doctest source code is
|
||||
// wrapped in a function to limit the number of AST errors. If we don't recurse into
|
||||
// functions, we would thing all top-level items (so basically nothing).
|
||||
fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) {
|
||||
if !info.has_global_allocator
|
||||
&& item.attrs.iter().any(|attr| attr.name_or_empty() == sym::global_allocator)
|
||||
{
|
||||
info.has_global_allocator = true;
|
||||
}
|
||||
match item.kind {
|
||||
ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => {
|
||||
if item.ident.name == sym::main {
|
||||
info.has_main_fn = true;
|
||||
}
|
||||
if let Some(ref body) = fn_item.body {
|
||||
for stmt in &body.stmts {
|
||||
match stmt.kind {
|
||||
ast::StmtKind::Item(ref item) => check_item(item, info, crate_name),
|
||||
ast::StmtKind::MacCall(..) => info.found_macro = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::ItemKind::ExternCrate(original) => {
|
||||
if !info.found_extern_crate
|
||||
&& let Some(ref crate_name) = crate_name
|
||||
{
|
||||
info.found_extern_crate = match original {
|
||||
Some(name) => name.as_str() == *crate_name,
|
||||
None => item.ident.as_str() == *crate_name,
|
||||
};
|
||||
}
|
||||
}
|
||||
ast::ItemKind::MacCall(..) => info.found_macro = true,
|
||||
ast::ItemKind::MacroDef(..) => info.has_macro_def = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This code cannot yet handle no_std test cases yet
|
||||
if dont_insert_main || already_has_main || prog.contains("![no_std]") {
|
||||
prog.push_str(everything_else);
|
||||
} else {
|
||||
let returns_result = everything_else.trim_end().ends_with("(())");
|
||||
// Give each doctest main function a unique name.
|
||||
// This is for example needed for the tooling around `-C instrument-coverage`.
|
||||
let inner_fn_name = if let Some(test_id) = test_id {
|
||||
format!("_doctest_main_{test_id}")
|
||||
} else {
|
||||
"_inner".into()
|
||||
};
|
||||
let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
|
||||
let (main_pre, main_post) = if returns_result {
|
||||
(
|
||||
format!(
|
||||
"fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n",
|
||||
),
|
||||
format!("\n}} {inner_fn_name}().unwrap() }}"),
|
||||
)
|
||||
} else if test_id.is_some() {
|
||||
(
|
||||
format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",),
|
||||
format!("\n}} {inner_fn_name}() }}"),
|
||||
)
|
||||
} else {
|
||||
("fn main() {\n".into(), "\n}".into())
|
||||
};
|
||||
// Note on newlines: We insert a line/newline *before*, and *after*
|
||||
// the doctest and adjust the `line_offset` accordingly.
|
||||
// In the case of `-C instrument-coverage`, this means that the generated
|
||||
// inner `main` function spans from the doctest opening codeblock to the
|
||||
// closing one. For example
|
||||
// /// ``` <- start of the inner main
|
||||
// /// <- code under doctest
|
||||
// /// ``` <- end of the inner main
|
||||
line_offset += 1;
|
||||
loop {
|
||||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => {
|
||||
check_item(&item, info, crate_name);
|
||||
|
||||
// add extra 4 spaces for each line to offset the code block
|
||||
let content = if opts.insert_indent_space {
|
||||
everything_else
|
||||
.lines()
|
||||
.map(|line| format!(" {}", line))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
} else {
|
||||
everything_else.to_string()
|
||||
};
|
||||
prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned());
|
||||
if info.has_main_fn && info.found_extern_crate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
parsing_result = ParsingResult::AstError;
|
||||
e.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The supplied item is only used for diagnostics,
|
||||
// which are swallowed here anyway.
|
||||
parser.maybe_consume_incorrect_semicolon(None);
|
||||
}
|
||||
|
||||
debug!("final doctest:\n{prog}");
|
||||
cancel_error_count(&psess);
|
||||
parsing_result
|
||||
}
|
||||
|
||||
(prog, line_offset, supports_color)
|
||||
#[derive(Default)]
|
||||
struct ParseSourceInfo {
|
||||
has_main_fn: bool,
|
||||
found_extern_crate: bool,
|
||||
found_macro: bool,
|
||||
supports_color: bool,
|
||||
has_global_allocator: bool,
|
||||
has_macro_def: bool,
|
||||
}
|
||||
|
||||
fn check_for_main_and_extern_crate(
|
||||
crate_name: Option<&str>,
|
||||
source: String,
|
||||
original_source_code: &str,
|
||||
everything_else: &str,
|
||||
crates: &str,
|
||||
edition: Edition,
|
||||
supports_color: &mut bool,
|
||||
) -> Result<(bool, bool), FatalError> {
|
||||
can_merge_doctests: bool,
|
||||
) -> Result<(ParseSourceInfo, bool), FatalError> {
|
||||
let result = rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::create_session_if_not_set_then(edition, |_| {
|
||||
use rustc_errors::emitter::{Emitter, HumanEmitter};
|
||||
use rustc_errors::DiagCtxt;
|
||||
use rustc_parse::parser::ForceCollect;
|
||||
use rustc_span::source_map::FilePathMapping;
|
||||
let mut info =
|
||||
ParseSourceInfo { found_extern_crate: crate_name.is_none(), ..Default::default() };
|
||||
|
||||
let filename = FileName::anon_source_code(&source);
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
false,
|
||||
);
|
||||
*supports_color =
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
|
||||
.supports_color();
|
||||
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
let psess = ParseSess::with_dcx(dcx, sm);
|
||||
|
||||
let mut found_main = false;
|
||||
let mut found_extern_crate = crate_name.is_none();
|
||||
let mut found_macro = false;
|
||||
|
||||
let mut parser = match new_parser_from_source_str(&psess, filename, source.clone()) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
errs.into_iter().for_each(|err| err.cancel());
|
||||
return (found_main, found_extern_crate, found_macro);
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => {
|
||||
if !found_main
|
||||
&& let ast::ItemKind::Fn(..) = item.kind
|
||||
&& item.ident.name == sym::main
|
||||
{
|
||||
found_main = true;
|
||||
}
|
||||
|
||||
if !found_extern_crate
|
||||
&& let ast::ItemKind::ExternCrate(original) = item.kind
|
||||
{
|
||||
// This code will never be reached if `crate_name` is none because
|
||||
// `found_extern_crate` is initialized to `true` if it is none.
|
||||
let crate_name = crate_name.unwrap();
|
||||
|
||||
match original {
|
||||
Some(name) => found_extern_crate = name.as_str() == crate_name,
|
||||
None => found_extern_crate = item.ident.as_str() == crate_name,
|
||||
}
|
||||
}
|
||||
|
||||
if !found_macro && let ast::ItemKind::MacCall(..) = item.kind {
|
||||
found_macro = true;
|
||||
}
|
||||
|
||||
if found_main && found_extern_crate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The supplied item is only used for diagnostics,
|
||||
// which are swallowed here anyway.
|
||||
parser.maybe_consume_incorrect_semicolon(None);
|
||||
let mut parsing_result =
|
||||
parse_source(format!("{crates}{everything_else}"), &mut info, &crate_name);
|
||||
// No need to double-check this if the "merged doctests" feature isn't enabled (so
|
||||
// before the 2024 edition).
|
||||
if can_merge_doctests && parsing_result != ParsingResult::Ok {
|
||||
// If we found an AST error, we want to ensure it's because of an expression being
|
||||
// used outside of a function.
|
||||
//
|
||||
// To do so, we wrap in a function in order to make sure that the doctest AST is
|
||||
// correct. For example, if your doctest is `foo::bar()`, if we don't wrap it in a
|
||||
// block, it would emit an AST error, which would be problematic for us since we
|
||||
// want to filter out such errors which aren't "real" errors.
|
||||
//
|
||||
// The end goal is to be able to merge as many doctests as possible as one for much
|
||||
// faster doctests run time.
|
||||
parsing_result = parse_source(
|
||||
format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"),
|
||||
&mut info,
|
||||
&crate_name,
|
||||
);
|
||||
}
|
||||
|
||||
// Reset errors so that they won't be reported as compiler bugs when dropping the
|
||||
// dcx. Any errors in the tests will be reported when the test file is compiled,
|
||||
// Note that we still need to cancel the errors above otherwise `Diag` will panic on
|
||||
// drop.
|
||||
psess.dcx().reset_err_count();
|
||||
|
||||
(found_main, found_extern_crate, found_macro)
|
||||
(info, parsing_result)
|
||||
})
|
||||
});
|
||||
let (already_has_main, already_has_extern_crate, found_macro) = result?;
|
||||
let (mut info, parsing_result) = match result {
|
||||
Err(..) | Ok((_, ParsingResult::Failed)) => return Err(FatalError),
|
||||
Ok((info, parsing_result)) => (info, parsing_result),
|
||||
};
|
||||
|
||||
// If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
|
||||
// see it. In that case, run the old text-based scan to see if they at least have a main
|
||||
// function written inside a macro invocation. See
|
||||
// https://github.com/rust-lang/rust/issues/56898
|
||||
let already_has_main = if found_macro && !already_has_main {
|
||||
source
|
||||
if info.found_macro
|
||||
&& !info.has_main_fn
|
||||
&& original_source_code
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let comment = line.find("//");
|
||||
if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line }
|
||||
})
|
||||
.any(|code| code.contains("fn main"))
|
||||
} else {
|
||||
already_has_main
|
||||
};
|
||||
{
|
||||
info.has_main_fn = true;
|
||||
}
|
||||
|
||||
Ok((already_has_main, already_has_extern_crate))
|
||||
Ok((info, parsing_result != ParsingResult::Ok))
|
||||
}
|
||||
|
||||
fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
|
||||
enum AttrKind {
|
||||
CrateAttr,
|
||||
Attr,
|
||||
}
|
||||
|
||||
/// Returns `Some` if the attribute is complete and `Some(true)` if it is an attribute that can be
|
||||
/// placed at the crate root.
|
||||
fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option<AttrKind> {
|
||||
if source.is_empty() {
|
||||
// Empty content so nothing to check in here...
|
||||
return true;
|
||||
return None;
|
||||
}
|
||||
let not_crate_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny];
|
||||
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::create_session_if_not_set_then(edition, |_| {
|
||||
use rustc_errors::emitter::HumanEmitter;
|
||||
|
|
@ -285,32 +463,75 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
|
|||
errs.into_iter().for_each(|err| err.cancel());
|
||||
// If there is an unclosed delimiter, an error will be returned by the
|
||||
// tokentrees.
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// If a parsing error happened, it's very likely that the attribute is incomplete.
|
||||
if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
|
||||
e.cancel();
|
||||
return false;
|
||||
}
|
||||
true
|
||||
let ret = match parser.parse_attribute(InnerAttrPolicy::Permitted) {
|
||||
Ok(attr) => {
|
||||
let attr_name = attr.name_or_empty();
|
||||
|
||||
if not_crate_attrs.contains(&attr_name) {
|
||||
// There is one exception to these attributes:
|
||||
// `#![allow(internal_features)]`. If this attribute is used, we need to
|
||||
// consider it only as a crate-level attribute.
|
||||
if attr_name == sym::allow
|
||||
&& let Some(list) = attr.meta_item_list()
|
||||
&& list.iter().any(|sub_attr| {
|
||||
sub_attr.name_or_empty().as_str() == "internal_features"
|
||||
})
|
||||
{
|
||||
Some(AttrKind::CrateAttr)
|
||||
} else {
|
||||
Some(AttrKind::Attr)
|
||||
}
|
||||
} else {
|
||||
Some(AttrKind::CrateAttr)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
None
|
||||
}
|
||||
};
|
||||
ret
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
.unwrap_or(None)
|
||||
}
|
||||
|
||||
fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
|
||||
fn handle_attr(mod_attr_pending: &mut String, source_info: &mut SourceInfo, edition: Edition) {
|
||||
if let Some(attr_kind) = check_if_attr_is_complete(mod_attr_pending, edition) {
|
||||
let push_to = match attr_kind {
|
||||
AttrKind::CrateAttr => &mut source_info.crate_attrs,
|
||||
AttrKind::Attr => &mut source_info.maybe_crate_attrs,
|
||||
};
|
||||
push_to.push_str(mod_attr_pending);
|
||||
push_to.push('\n');
|
||||
// If it's complete, then we can clear the pending content.
|
||||
mod_attr_pending.clear();
|
||||
} else if mod_attr_pending.ends_with('\\') {
|
||||
mod_attr_pending.push('n');
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SourceInfo {
|
||||
crate_attrs: String,
|
||||
maybe_crate_attrs: String,
|
||||
crates: String,
|
||||
everything_else: String,
|
||||
}
|
||||
|
||||
fn partition_source(s: &str, edition: Edition) -> SourceInfo {
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum PartitionState {
|
||||
Attrs,
|
||||
Crates,
|
||||
Other,
|
||||
}
|
||||
let mut source_info = SourceInfo::default();
|
||||
let mut state = PartitionState::Attrs;
|
||||
let mut before = String::new();
|
||||
let mut crates = String::new();
|
||||
let mut after = String::new();
|
||||
|
||||
let mut mod_attr_pending = String::new();
|
||||
|
||||
for line in s.lines() {
|
||||
|
|
@ -321,12 +542,9 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
|
|||
match state {
|
||||
PartitionState::Attrs => {
|
||||
state = if trimline.starts_with("#![") {
|
||||
if !check_if_attr_is_complete(line, edition) {
|
||||
mod_attr_pending = line.to_owned();
|
||||
} else {
|
||||
mod_attr_pending.clear();
|
||||
}
|
||||
PartitionState::Attrs
|
||||
mod_attr_pending = line.to_owned();
|
||||
handle_attr(&mut mod_attr_pending, &mut source_info, edition);
|
||||
continue;
|
||||
} else if trimline.chars().all(|c| c.is_whitespace())
|
||||
|| (trimline.starts_with("//") && !trimline.starts_with("///"))
|
||||
{
|
||||
|
|
@ -341,15 +559,10 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
|
|||
// If not, then we append the new line into the pending attribute to check
|
||||
// if this time it's complete...
|
||||
mod_attr_pending.push_str(line);
|
||||
if !trimline.is_empty()
|
||||
&& check_if_attr_is_complete(&mod_attr_pending, edition)
|
||||
{
|
||||
// If it's complete, then we can clear the pending content.
|
||||
mod_attr_pending.clear();
|
||||
if !trimline.is_empty() {
|
||||
handle_attr(&mut mod_attr_pending, &mut source_info, edition);
|
||||
}
|
||||
// In any case, this is considered as `PartitionState::Attrs` so it's
|
||||
// prepended before rustdoc's inserts.
|
||||
PartitionState::Attrs
|
||||
continue;
|
||||
} else {
|
||||
PartitionState::Other
|
||||
}
|
||||
|
|
@ -371,23 +584,25 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
|
|||
|
||||
match state {
|
||||
PartitionState::Attrs => {
|
||||
before.push_str(line);
|
||||
before.push('\n');
|
||||
source_info.crate_attrs.push_str(line);
|
||||
source_info.crate_attrs.push('\n');
|
||||
}
|
||||
PartitionState::Crates => {
|
||||
crates.push_str(line);
|
||||
crates.push('\n');
|
||||
source_info.crates.push_str(line);
|
||||
source_info.crates.push('\n');
|
||||
}
|
||||
PartitionState::Other => {
|
||||
after.push_str(line);
|
||||
after.push('\n');
|
||||
source_info.everything_else.push_str(line);
|
||||
source_info.everything_else.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("before:\n{before}");
|
||||
debug!("crates:\n{crates}");
|
||||
debug!("after:\n{after}");
|
||||
source_info.everything_else = source_info.everything_else.trim().to_string();
|
||||
|
||||
(before, after, crates)
|
||||
debug!("crate_attrs:\n{}{}", source_info.crate_attrs, source_info.maybe_crate_attrs);
|
||||
debug!("crates:\n{}", source_info.crates);
|
||||
debug!("after:\n{}", source_info.everything_else);
|
||||
|
||||
source_info
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,29 @@
|
|||
//! Doctest functionality used only for doctests in `.md` Markdown files.
|
||||
|
||||
use std::fs::read_to_string;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use rustc_span::FileName;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::{
|
||||
generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest,
|
||||
generate_args_file, CreateRunnableDocTests, DocTestVisitor, GlobalTestOptions, ScrapedDocTest,
|
||||
};
|
||||
use crate::config::Options;
|
||||
use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine};
|
||||
|
||||
struct MdCollector {
|
||||
tests: Vec<ScrapedDoctest>,
|
||||
tests: Vec<ScrapedDocTest>,
|
||||
cur_path: Vec<String>,
|
||||
filename: FileName,
|
||||
}
|
||||
|
||||
impl DoctestVisitor for MdCollector {
|
||||
impl DocTestVisitor for MdCollector {
|
||||
fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) {
|
||||
let filename = self.filename.clone();
|
||||
// First line of Markdown is line 1.
|
||||
let line = 1 + rel_line.offset();
|
||||
self.tests.push(ScrapedDoctest {
|
||||
filename,
|
||||
line,
|
||||
logical_path: self.cur_path.clone(),
|
||||
langstr: config,
|
||||
text: test,
|
||||
});
|
||||
self.tests.push(ScrapedDocTest::new(filename, line, self.cur_path.clone(), config, test));
|
||||
}
|
||||
|
||||
fn visit_header(&mut self, name: &str, level: u32) {
|
||||
|
|
@ -118,8 +113,16 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
|
|||
None,
|
||||
);
|
||||
|
||||
let mut collector = CreateRunnableDoctests::new(options.clone(), opts);
|
||||
let mut collector = CreateRunnableDocTests::new(options.clone(), opts);
|
||||
md_collector.tests.into_iter().for_each(|t| collector.add_test(t));
|
||||
crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
|
||||
let CreateRunnableDocTests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } =
|
||||
collector;
|
||||
crate::doctest::run_tests(
|
||||
opts,
|
||||
&rustdoc_options,
|
||||
&Arc::new(Mutex::new(Vec::new())),
|
||||
standalone_tests,
|
||||
mergeable_tests,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
269
src/librustdoc/doctest/runner.rs
Normal file
269
src/librustdoc/doctest/runner.rs
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_span::edition::Edition;
|
||||
|
||||
use crate::doctest::{
|
||||
run_test, DocTestBuilder, GlobalTestOptions, IndividualTestOptions, RunnableDocTest,
|
||||
RustdocOptions, ScrapedDocTest, TestFailure, UnusedExterns,
|
||||
};
|
||||
use crate::html::markdown::{Ignore, LangString};
|
||||
|
||||
/// Convenient type to merge compatible doctests into one.
|
||||
pub(crate) struct DocTestRunner {
|
||||
crate_attrs: FxHashSet<String>,
|
||||
ids: String,
|
||||
output: String,
|
||||
supports_color: bool,
|
||||
nb_tests: usize,
|
||||
}
|
||||
|
||||
impl DocTestRunner {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
crate_attrs: FxHashSet::default(),
|
||||
ids: String::new(),
|
||||
output: String::new(),
|
||||
supports_color: true,
|
||||
nb_tests: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_test(
|
||||
&mut self,
|
||||
doctest: &DocTestBuilder,
|
||||
scraped_test: &ScrapedDocTest,
|
||||
target_str: &str,
|
||||
) {
|
||||
let ignore = match scraped_test.langstr.ignore {
|
||||
Ignore::All => true,
|
||||
Ignore::None => false,
|
||||
Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
|
||||
};
|
||||
if !ignore {
|
||||
for line in doctest.crate_attrs.split('\n') {
|
||||
self.crate_attrs.insert(line.to_string());
|
||||
}
|
||||
}
|
||||
if !self.ids.is_empty() {
|
||||
self.ids.push(',');
|
||||
}
|
||||
self.ids.push_str(&format!(
|
||||
"{}::TEST",
|
||||
generate_mergeable_doctest(
|
||||
doctest,
|
||||
scraped_test,
|
||||
ignore,
|
||||
self.nb_tests,
|
||||
&mut self.output
|
||||
),
|
||||
));
|
||||
self.supports_color &= doctest.supports_color;
|
||||
self.nb_tests += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn run_merged_tests(
|
||||
&mut self,
|
||||
test_options: IndividualTestOptions,
|
||||
edition: Edition,
|
||||
opts: &GlobalTestOptions,
|
||||
test_args: &[String],
|
||||
rustdoc_options: &RustdocOptions,
|
||||
) -> Result<bool, ()> {
|
||||
let mut code = "\
|
||||
#![allow(unused_extern_crates)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(test)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(coverage_attribute)]
|
||||
"
|
||||
.to_string();
|
||||
|
||||
for crate_attr in &self.crate_attrs {
|
||||
code.push_str(crate_attr);
|
||||
code.push('\n');
|
||||
}
|
||||
|
||||
if opts.attrs.is_empty() {
|
||||
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
|
||||
// lints that are commonly triggered in doctests. The crate-level test attributes are
|
||||
// commonly used to make tests fail in case they trigger warnings, so having this there in
|
||||
// that case may cause some tests to pass when they shouldn't have.
|
||||
code.push_str("#![allow(unused)]\n");
|
||||
}
|
||||
|
||||
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
|
||||
for attr in &opts.attrs {
|
||||
code.push_str(&format!("#![{attr}]\n"));
|
||||
}
|
||||
|
||||
code.push_str("extern crate test;\n");
|
||||
|
||||
let test_args =
|
||||
test_args.iter().map(|arg| format!("{arg:?}.to_string(),")).collect::<String>();
|
||||
write!(
|
||||
code,
|
||||
"\
|
||||
{output}
|
||||
|
||||
mod __doctest_mod {{
|
||||
use std::sync::OnceLock;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
pub const RUN_OPTION: &str = \"*doctest-inner-test\";
|
||||
pub const BIN_OPTION: &str = \"*doctest-bin-path\";
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn doctest_path() -> Option<&'static PathBuf> {{
|
||||
self::BINARY_PATH.get()
|
||||
}}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{
|
||||
let out = std::process::Command::new(bin)
|
||||
.arg(self::RUN_OPTION)
|
||||
.arg(test_nb.to_string())
|
||||
.output()
|
||||
.expect(\"failed to run command\");
|
||||
if !out.status.success() {{
|
||||
Err(String::from_utf8_lossy(&out.stderr).to_string())
|
||||
}} else {{
|
||||
Ok(())
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
#[rustc_main]
|
||||
#[coverage(off)]
|
||||
fn main() -> std::process::ExitCode {{
|
||||
const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}];
|
||||
let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION);
|
||||
let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION);
|
||||
let test_args = &[{test_args}];
|
||||
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
while let Some(arg) = args.next() {{
|
||||
if arg == bin_marker {{
|
||||
let Some(binary) = args.next() else {{
|
||||
panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION);
|
||||
}};
|
||||
if crate::__doctest_mod::BINARY_PATH.set(binary.into()).is_err() {{
|
||||
panic!(\"`{{}}` option was used more than once\", bin_marker.to_string_lossy());
|
||||
}}
|
||||
return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None));
|
||||
}} else if arg == test_marker {{
|
||||
let Some(nb_test) = args.next() else {{
|
||||
panic!(\"missing argument after `{{}}`\", __doctest_mod::RUN_OPTION);
|
||||
}};
|
||||
if let Some(nb_test) = nb_test.to_str().and_then(|nb| nb.parse::<usize>().ok()) {{
|
||||
if let Some(test) = TESTS.get(nb_test) {{
|
||||
if let test::StaticTestFn(f) = test.testfn {{
|
||||
return std::process::Termination::report(f());
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
panic!(\"Unexpected value after `{{}}`\", __doctest_mod::RUN_OPTION);
|
||||
}}
|
||||
}}
|
||||
|
||||
eprintln!(\"WARNING: No argument provided so doctests will be run in the same process\");
|
||||
std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None))
|
||||
}}",
|
||||
nb_tests = self.nb_tests,
|
||||
output = self.output,
|
||||
ids = self.ids,
|
||||
)
|
||||
.expect("failed to generate test code");
|
||||
let runnable_test = RunnableDocTest {
|
||||
full_test_code: code,
|
||||
full_test_line_offset: 0,
|
||||
test_opts: test_options,
|
||||
global_opts: opts.clone(),
|
||||
langstr: LangString::default(),
|
||||
line: 0,
|
||||
edition,
|
||||
no_run: false,
|
||||
is_multiple_tests: true,
|
||||
};
|
||||
let ret =
|
||||
run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {});
|
||||
if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Push new doctest content into `output`. Returns the test ID for this doctest.
|
||||
fn generate_mergeable_doctest(
|
||||
doctest: &DocTestBuilder,
|
||||
scraped_test: &ScrapedDocTest,
|
||||
ignore: bool,
|
||||
id: usize,
|
||||
output: &mut String,
|
||||
) -> String {
|
||||
let test_id = format!("__doctest_{id}");
|
||||
|
||||
if ignore {
|
||||
// We generate nothing else.
|
||||
writeln!(output, "mod {test_id} {{\n").unwrap();
|
||||
} else {
|
||||
writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs)
|
||||
.unwrap();
|
||||
if scraped_test.langstr.no_run {
|
||||
// To prevent having warnings about unused items since they're not called.
|
||||
writeln!(output, "#![allow(unused)]").unwrap();
|
||||
}
|
||||
if doctest.has_main_fn {
|
||||
output.push_str(&doctest.everything_else);
|
||||
} else {
|
||||
let returns_result = if doctest.everything_else.trim_end().ends_with("(())") {
|
||||
"-> Result<(), impl core::fmt::Debug>"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
write!(
|
||||
output,
|
||||
"\
|
||||
fn main() {returns_result} {{
|
||||
{}
|
||||
}}",
|
||||
doctest.everything_else
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
let not_running = ignore || scraped_test.langstr.no_run;
|
||||
writeln!(
|
||||
output,
|
||||
"
|
||||
#[rustc_test_marker = {test_name:?}]
|
||||
pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest(
|
||||
{test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic},
|
||||
test::StaticTestFn(
|
||||
#[coverage(off)]
|
||||
|| {{{runner}}},
|
||||
));
|
||||
}}",
|
||||
test_name = scraped_test.name,
|
||||
file = scraped_test.path(),
|
||||
line = scraped_test.line,
|
||||
no_run = scraped_test.langstr.no_run,
|
||||
should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic,
|
||||
// Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
|
||||
// don't give it the function to run.
|
||||
runner = if not_running {
|
||||
"test::assert_test_result(Ok::<(), String>(()))".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"
|
||||
if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
|
||||
test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}))
|
||||
}} else {{
|
||||
test::assert_test_result(self::main())
|
||||
}}
|
||||
",
|
||||
)
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
test_id
|
||||
}
|
||||
|
|
@ -14,14 +14,14 @@ use rustc_session::Session;
|
|||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
|
||||
|
||||
use super::{DoctestVisitor, ScrapedDoctest};
|
||||
use super::{DocTestVisitor, ScrapedDocTest};
|
||||
use crate::clean::types::AttributesExt;
|
||||
use crate::clean::Attributes;
|
||||
use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine};
|
||||
|
||||
struct RustCollector {
|
||||
source_map: Lrc<SourceMap>,
|
||||
tests: Vec<ScrapedDoctest>,
|
||||
tests: Vec<ScrapedDocTest>,
|
||||
cur_path: Vec<String>,
|
||||
position: Span,
|
||||
}
|
||||
|
|
@ -48,16 +48,16 @@ impl RustCollector {
|
|||
}
|
||||
}
|
||||
|
||||
impl DoctestVisitor for RustCollector {
|
||||
impl DocTestVisitor for RustCollector {
|
||||
fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) {
|
||||
let line = self.get_base_line() + rel_line.offset();
|
||||
self.tests.push(ScrapedDoctest {
|
||||
filename: self.get_filename(),
|
||||
self.tests.push(ScrapedDocTest::new(
|
||||
self.get_filename(),
|
||||
line,
|
||||
logical_path: self.cur_path.clone(),
|
||||
langstr: config,
|
||||
text: test,
|
||||
});
|
||||
self.cur_path.clone(),
|
||||
config,
|
||||
test,
|
||||
));
|
||||
}
|
||||
|
||||
fn visit_header(&mut self, _name: &str, _level: u32) {}
|
||||
|
|
@ -89,7 +89,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
|
|||
Self { sess, map, codes, enable_per_target_ignores, tcx, collector }
|
||||
}
|
||||
|
||||
pub fn collect_crate(mut self) -> Vec<ScrapedDoctest> {
|
||||
pub fn collect_crate(mut self) -> Vec<ScrapedDocTest> {
|
||||
let tcx = self.tcx;
|
||||
self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| {
|
||||
tcx.hir().walk_toplevel_module(this)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,27 @@ use std::path::PathBuf;
|
|||
|
||||
use rustc_span::edition::DEFAULT_EDITION;
|
||||
|
||||
use super::{make_test, GlobalTestOptions};
|
||||
use super::{DocTestBuilder, GlobalTestOptions};
|
||||
|
||||
fn make_test(
|
||||
test_code: &str,
|
||||
crate_name: Option<&str>,
|
||||
dont_insert_main: bool,
|
||||
opts: &GlobalTestOptions,
|
||||
test_id: Option<&str>,
|
||||
) -> (String, usize) {
|
||||
let doctest = DocTestBuilder::new(
|
||||
test_code,
|
||||
crate_name,
|
||||
DEFAULT_EDITION,
|
||||
false,
|
||||
test_id.map(|s| s.to_string()),
|
||||
None,
|
||||
);
|
||||
let (code, line_offset) =
|
||||
doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
|
||||
(code, line_offset)
|
||||
}
|
||||
|
||||
/// Default [`GlobalTestOptions`] for these unit tests.
|
||||
fn default_global_opts(crate_name: impl Into<String>) -> GlobalTestOptions {
|
||||
|
|
@ -25,7 +45,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +60,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +79,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +96,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +114,7 @@ use std::*;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("std"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +133,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +150,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +170,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
|
||||
// Adding more will also bump the returned line offset.
|
||||
|
|
@ -164,7 +184,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 4));
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +201,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +217,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +233,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +247,7 @@ assert_eq!(2+2, 4);";
|
|||
//Ceci n'est pas une `fn main`
|
||||
assert_eq!(2+2, 4);"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, true, &opts, None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +265,7 @@ assert_eq!(2+2, 4);
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +285,7 @@ assert_eq!(asdf::foo, 4);
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +303,7 @@ test_wrapper! {
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, Some("my_crate"), false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +323,7 @@ io::stdin().read_line(&mut input)?;
|
|||
Ok::<(), io:Error>(())
|
||||
} _inner().unwrap() }"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -317,8 +337,7 @@ fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() {
|
|||
assert_eq!(2+2, 4);
|
||||
} _doctest_main__some_unique_name() }"
|
||||
.to_string();
|
||||
let (output, len, _) =
|
||||
make_test(input, None, false, &opts, DEFAULT_EDITION, Some("_some_unique_name"));
|
||||
let (output, len) = make_test(input, None, false, &opts, Some("_some_unique_name"));
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -337,7 +356,7 @@ fn main() {
|
|||
eprintln!(\"hello anan\");
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -357,6 +376,6 @@ fn main() {
|
|||
eprintln!(\"hello anan\");
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,14 +297,16 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
|
|||
attrs: vec![],
|
||||
args_file: PathBuf::new(),
|
||||
};
|
||||
let (test, _, _) = doctest::make_test(&test, krate, false, &opts, edition, None);
|
||||
let doctest = doctest::DocTestBuilder::new(&test, krate, edition, false, None, None);
|
||||
let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
|
||||
let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" };
|
||||
|
||||
let test_escaped = small_url_encode(test);
|
||||
Some(format!(
|
||||
"<a class=\"test-arrow\" \
|
||||
target=\"_blank\" \
|
||||
href=\"{url}?code={test_escaped}{channel}&edition={edition}\">Run</a>",
|
||||
title=\"Run code\" \
|
||||
href=\"{url}?code={test_escaped}{channel}&edition={edition}\"></a>",
|
||||
))
|
||||
});
|
||||
|
||||
|
|
@ -736,7 +738,7 @@ impl MdRelLine {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_testable_code<T: doctest::DoctestVisitor>(
|
||||
pub(crate) fn find_testable_code<T: doctest::DocTestVisitor>(
|
||||
doc: &str,
|
||||
tests: &mut T,
|
||||
error_codes: ErrorCodes,
|
||||
|
|
@ -746,7 +748,7 @@ pub(crate) fn find_testable_code<T: doctest::DoctestVisitor>(
|
|||
find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
|
||||
}
|
||||
|
||||
pub(crate) fn find_codes<T: doctest::DoctestVisitor>(
|
||||
pub(crate) fn find_codes<T: doctest::DocTestVisitor>(
|
||||
doc: &str,
|
||||
tests: &mut T,
|
||||
error_codes: ErrorCodes,
|
||||
|
|
@ -867,6 +869,7 @@ pub(crate) struct LangString {
|
|||
pub(crate) rust: bool,
|
||||
pub(crate) test_harness: bool,
|
||||
pub(crate) compile_fail: bool,
|
||||
pub(crate) standalone: bool,
|
||||
pub(crate) error_codes: Vec<String>,
|
||||
pub(crate) edition: Option<Edition>,
|
||||
pub(crate) added_classes: Vec<String>,
|
||||
|
|
@ -1189,6 +1192,7 @@ impl Default for LangString {
|
|||
rust: true,
|
||||
test_harness: false,
|
||||
compile_fail: false,
|
||||
standalone: false,
|
||||
error_codes: Vec::new(),
|
||||
edition: None,
|
||||
added_classes: Vec::new(),
|
||||
|
|
@ -1258,6 +1262,10 @@ impl LangString {
|
|||
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
||||
data.no_run = true;
|
||||
}
|
||||
LangStringToken::LangToken("standalone") => {
|
||||
data.standalone = true;
|
||||
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
||||
}
|
||||
LangStringToken::LangToken(x) if x.starts_with("edition") => {
|
||||
data.edition = x[7..].parse::<Edition>().ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,10 +104,6 @@ nav.sub {
|
|||
--code-highlight-doc-comment-color: #4d4d4c;
|
||||
--src-line-numbers-span-color: #c67e2d;
|
||||
--src-line-number-highlighted-background-color: #fdffd3;
|
||||
--test-arrow-color: #f5f5f5;
|
||||
--test-arrow-background-color: rgba(78, 139, 202, 0.2);
|
||||
--test-arrow-hover-color: #f5f5f5;
|
||||
--test-arrow-hover-background-color: rgb(78, 139, 202);
|
||||
--target-background-color: #fdffd3;
|
||||
--target-border-color: #ad7c37;
|
||||
--kbd-color: #000;
|
||||
|
|
@ -210,10 +206,6 @@ nav.sub {
|
|||
--code-highlight-doc-comment-color: #8ca375;
|
||||
--src-line-numbers-span-color: #3b91e2;
|
||||
--src-line-number-highlighted-background-color: #0a042f;
|
||||
--test-arrow-color: #dedede;
|
||||
--test-arrow-background-color: rgba(78, 139, 202, 0.2);
|
||||
--test-arrow-hover-color: #dedede;
|
||||
--test-arrow-hover-background-color: #4e8bca;
|
||||
--target-background-color: #494a3d;
|
||||
--target-border-color: #bb7410;
|
||||
--kbd-color: #000;
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ details:not(.toggle) summary {
|
|||
margin-bottom: .6em;
|
||||
}
|
||||
|
||||
code, pre, a.test-arrow, .code-header {
|
||||
code, pre, .code-header {
|
||||
font-family: "Source Code Pro", monospace;
|
||||
}
|
||||
.docblock code, .docblock-short code {
|
||||
|
|
@ -946,8 +946,8 @@ because of the `[-]` element which would overlap with it. */
|
|||
.main-heading a:hover,
|
||||
.example-wrap .rust a:hover,
|
||||
.all-items a:hover,
|
||||
.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
|
||||
.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
|
||||
.docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
|
||||
.docblock-short a:not(.scrape-help):not(.tooltip):hover,
|
||||
.item-info a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -1461,22 +1461,17 @@ documentation. */
|
|||
z-index: 1;
|
||||
}
|
||||
a.test-arrow {
|
||||
padding: 5px 7px;
|
||||
border-radius: var(--button-border-radius);
|
||||
font-size: 1rem;
|
||||
color: var(--test-arrow-color);
|
||||
background-color: var(--test-arrow-background-color);
|
||||
height: var(--copy-path-height);
|
||||
padding: 6px 4px 0 11px;
|
||||
}
|
||||
a.test-arrow:hover {
|
||||
color: var(--test-arrow-hover-color);
|
||||
background-color: var(--test-arrow-hover-background-color);
|
||||
a.test-arrow::before {
|
||||
content: url('data:image/svg+xml,<svg viewBox="0 0 20 20" width="18" height="20" \
|
||||
xmlns="http://www.w3.org/2000/svg"><path d="M0 0l18 10-18 10z"/></svg>');
|
||||
}
|
||||
.example-wrap .button-holder {
|
||||
display: flex;
|
||||
}
|
||||
.example-wrap:hover > .test-arrow {
|
||||
padding: 2px 7px;
|
||||
}
|
||||
|
||||
/*
|
||||
On iPad, the ":hover" state sticks around, making things work not greatly. Do work around
|
||||
it, we move it into this media query. More information can be found at:
|
||||
|
|
@ -1486,29 +1481,34 @@ However, using `@media (hover: hover)` makes this rule never to be applied in GU
|
|||
instead, we check that it's not a "finger" cursor.
|
||||
*/
|
||||
@media not (pointer: coarse) {
|
||||
.example-wrap:hover > .test-arrow, .example-wrap:hover > .button-holder {
|
||||
.example-wrap:hover > a.test-arrow, .example-wrap:hover > .button-holder {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
.example-wrap .button-holder.keep-visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.example-wrap .button-holder .copy-button {
|
||||
color: var(--copy-path-button-color);
|
||||
.example-wrap .button-holder .copy-button, .example-wrap .test-arrow {
|
||||
background: var(--main-background-color);
|
||||
cursor: pointer;
|
||||
border-radius: var(--button-border-radius);
|
||||
height: var(--copy-path-height);
|
||||
width: var(--copy-path-width);
|
||||
}
|
||||
.example-wrap .button-holder .copy-button {
|
||||
margin-left: var(--button-left-margin);
|
||||
padding: 2px 0 0 4px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
border-radius: var(--button-border-radius);
|
||||
}
|
||||
.example-wrap .button-holder .copy-button::before,
|
||||
.example-wrap .test-arrow::before {
|
||||
filter: var(--copy-path-img-filter);
|
||||
}
|
||||
.example-wrap .button-holder .copy-button::before {
|
||||
filter: var(--copy-path-img-filter);
|
||||
content: var(--clipboard-image);
|
||||
}
|
||||
.example-wrap .button-holder .copy-button:hover::before {
|
||||
.example-wrap .button-holder .copy-button:hover::before,
|
||||
.example-wrap .test-arrow:hover::before {
|
||||
filter: var(--copy-path-img-hover-filter);
|
||||
}
|
||||
.example-wrap .button-holder .copy-button.clicked::before {
|
||||
|
|
@ -2552,10 +2552,6 @@ by default.
|
|||
--code-highlight-doc-comment-color: #4d4d4c;
|
||||
--src-line-numbers-span-color: #c67e2d;
|
||||
--src-line-number-highlighted-background-color: #fdffd3;
|
||||
--test-arrow-color: #f5f5f5;
|
||||
--test-arrow-background-color: rgba(78, 139, 202, 0.2);
|
||||
--test-arrow-hover-color: #f5f5f5;
|
||||
--test-arrow-hover-background-color: rgb(78, 139, 202);
|
||||
--target-background-color: #fdffd3;
|
||||
--target-border-color: #ad7c37;
|
||||
--kbd-color: #000;
|
||||
|
|
@ -2658,10 +2654,6 @@ by default.
|
|||
--code-highlight-doc-comment-color: #8ca375;
|
||||
--src-line-numbers-span-color: #3b91e2;
|
||||
--src-line-number-highlighted-background-color: #0a042f;
|
||||
--test-arrow-color: #dedede;
|
||||
--test-arrow-background-color: rgba(78, 139, 202, 0.2);
|
||||
--test-arrow-hover-color: #dedede;
|
||||
--test-arrow-hover-background-color: #4e8bca;
|
||||
--target-background-color: #494a3d;
|
||||
--target-border-color: #bb7410;
|
||||
--kbd-color: #000;
|
||||
|
|
@ -2771,10 +2763,6 @@ Original by Dempfi (https://github.com/dempfi/ayu)
|
|||
--code-highlight-doc-comment-color: #a1ac88;
|
||||
--src-line-numbers-span-color: #5c6773;
|
||||
--src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
|
||||
--test-arrow-color: #788797;
|
||||
--test-arrow-background-color: rgba(57, 175, 215, 0.09);
|
||||
--test-arrow-hover-color: #c5c5c5;
|
||||
--test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
|
||||
--target-background-color: rgba(255, 236, 164, 0.06);
|
||||
--target-border-color: rgba(255, 180, 76, 0.85);
|
||||
--kbd-color: #c5c5c5;
|
||||
|
|
|
|||
|
|
@ -1835,10 +1835,14 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
|
|||
function getExampleWrap(event) {
|
||||
let elem = event.target;
|
||||
while (!hasClass(elem, "example-wrap")) {
|
||||
elem = elem.parentElement;
|
||||
if (elem === document.body || hasClass(elem, "docblock")) {
|
||||
if (elem === document.body ||
|
||||
elem.tagName === "A" ||
|
||||
elem.tagName === "BUTTON" ||
|
||||
hasClass(elem, "docblock")
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
elem = elem.parentElement;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub(crate) struct Tests {
|
|||
pub(crate) found_tests: usize,
|
||||
}
|
||||
|
||||
impl crate::doctest::DoctestVisitor for Tests {
|
||||
impl crate::doctest::DocTestVisitor for Tests {
|
||||
fn visit_test(&mut self, _: String, config: LangString, _: MdRelLine) {
|
||||
if config.rust && config.ignore == Ignore::None {
|
||||
self.found_tests += 1;
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||
ty::RawPtr(_, _) => Res::Primitive(RawPointer),
|
||||
ty::Ref(..) => Res::Primitive(Reference),
|
||||
ty::FnDef(..) => panic!("type alias to a function definition"),
|
||||
ty::FnPtr(_) => Res::Primitive(Fn),
|
||||
ty::FnPtr(..) => Res::Primitive(Fn),
|
||||
ty::Never => Res::Primitive(Never),
|
||||
ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => {
|
||||
Res::from_def_id(self.cx.tcx, did)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit ccf4c38bdd73f1a37ec266c73bdaef80e39f8cf6
|
||||
Subproject commit 57ae1a3474057fead2c438928ed368b3740bf0ec
|
||||
|
|
@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
}
|
||||
|
||||
match cast_from.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
|
||||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
_ => { /* continue to checks */ },
|
||||
}
|
||||
|
||||
if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() {
|
||||
if let ty::FnDef(..) | ty::FnPtr(..) = cast_from.kind() {
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
_ => return,
|
||||
}
|
||||
match cast_from.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'
|
|||
// We can't use `Ty::fn_sig` because it automatically performs args, this may result in FNs.
|
||||
match node_ty.kind() {
|
||||
ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).instantiate_identity()),
|
||||
ty::FnPtr(fn_sig) => Some(*fn_sig),
|
||||
ty::FnPtr(sig_tys, hdr) => Some(sig_tys.with(*hdr)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -872,7 +872,7 @@ impl TyCoercionStability {
|
|||
| ty::Pat(..)
|
||||
| ty::Float(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Str
|
||||
| ty::Slice(..)
|
||||
| ty::Adt(..)
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
|
|||
|
||||
cx.tcx.fn_sig(def).skip_binder().skip_binder()
|
||||
},
|
||||
ty::FnPtr(sig) => sig.skip_binder(),
|
||||
ty::FnPtr(sig_tys, hdr) => sig_tys.with(*hdr).skip_binder(),
|
||||
ty::Closure(_, subs) => cx
|
||||
.tcx
|
||||
.signature_unclosure(subs.as_closure().sig(), Safety::Safe)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fn try_get_caller_ty_name_and_method_name(
|
|||
fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
|
||||
match map_closure_ty.kind() {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) => {
|
||||
let map_closure_sig = match map_closure_ty.kind() {
|
||||
ty::Closure(_, args) => args.as_closure().sig(),
|
||||
_ => map_closure_ty.fn_sig(cx.tcx),
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
|
|||
ExprKind::Call(func, _) => {
|
||||
let typ = self.cx.typeck_results().expr_ty(func);
|
||||
match typ.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let sig = typ.fn_sig(self.cx.tcx);
|
||||
if self.cx.tcx.instantiate_bound_regions_with_erased(sig).output().kind() == &ty::Never {
|
||||
self.report_diverging_sub_expr(e);
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
ExprKind::Call(path_expr, _) => {
|
||||
let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
|
||||
ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
|
||||
ty::FnPtr(sig) => sig,
|
||||
ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
|
||||
_ => return Continue(Descend::Yes),
|
||||
};
|
||||
if sig.safety() == Safety::Unsafe {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ fn check_arguments<'tcx>(
|
|||
fn_kind: &str,
|
||||
) {
|
||||
match type_definition.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
|
||||
for (argument, parameter) in iter::zip(arguments, parameters) {
|
||||
match parameter.kind() {
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
|
|||
/// Returns `true` if the given type is an `unsafe` function.
|
||||
pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe,
|
||||
ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -721,7 +721,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
|
|||
cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args),
|
||||
cx.tcx.opt_parent(def_id),
|
||||
),
|
||||
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
|
||||
ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)),
|
||||
ty::Dynamic(bounds, _, _) => {
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
match bounds.principal() {
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
|||
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => {
|
||||
self.is_unsafe = true;
|
||||
},
|
||||
ty::FnPtr(sig) if sig.safety() == Safety::Unsafe => self.is_unsafe = true,
|
||||
ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true,
|
||||
_ => walk_expr(self, e),
|
||||
},
|
||||
ExprKind::Path(ref p)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//! These structs are a subset of the ones found in `rustc_errors::json`.
|
||||
//! They are only used for deserialization of JSON output provided by libtest.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
|
@ -127,11 +126,10 @@ pub fn extract_rendered(output: &str) -> String {
|
|||
// Ignore the notification.
|
||||
None
|
||||
} else {
|
||||
print!(
|
||||
"failed to decode compiler output as json: line: {}\noutput: {}",
|
||||
line, output
|
||||
);
|
||||
panic!()
|
||||
// This function is called for both compiler and non-compiler output,
|
||||
// so if the line isn't recognized as JSON from the compiler then
|
||||
// just print it as-is.
|
||||
Some(format!("{line}\n"))
|
||||
}
|
||||
} else {
|
||||
// preserve non-JSON lines, such as ICEs
|
||||
|
|
|
|||
|
|
@ -3027,11 +3027,17 @@ impl<'test> TestCx<'test> {
|
|||
const PREFIX: &str = "MONO_ITEM ";
|
||||
const CGU_MARKER: &str = "@@";
|
||||
|
||||
// Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
|
||||
// To prevent the current dir from leaking, we just replace the entire path to the test
|
||||
// file with TEST_PATH.
|
||||
let actual: Vec<MonoItem> = proc_res
|
||||
.stdout
|
||||
.lines()
|
||||
.filter(|line| line.starts_with(PREFIX))
|
||||
.map(|line| str_to_mono_item(line, true))
|
||||
.map(|line| {
|
||||
line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
|
||||
})
|
||||
.map(|line| str_to_mono_item(&line, true))
|
||||
.collect();
|
||||
|
||||
let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
|
||||
|
|
@ -3729,15 +3735,14 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
|
||||
if self.config.bless {
|
||||
cmd.env("RUSTC_BLESS_TEST", "--bless");
|
||||
// Assume this option is active if the environment variable is "defined", with _any_ value.
|
||||
// As an example, a `Makefile` can use this option by:
|
||||
// If we're running in `--bless` mode, set an environment variable to tell
|
||||
// `run_make_support` to bless snapshot files instead of checking them.
|
||||
//
|
||||
// ifdef RUSTC_BLESS_TEST
|
||||
// cp "$(TMPDIR)"/actual_something.ext expected_something.ext
|
||||
// else
|
||||
// $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
|
||||
// endif
|
||||
// The value is this test's source directory, because the support code
|
||||
// will need that path in order to bless the _original_ snapshot files,
|
||||
// not the copies in `rmake_out`.
|
||||
// (See <https://github.com/rust-lang/rust/issues/129038>.)
|
||||
cmd.env("RUSTC_BLESS_TEST", &self.testpaths.file);
|
||||
}
|
||||
|
||||
if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?;
|
||||
f.write_all(ARM32_SHIM.as_bytes())?;
|
||||
writeln!(&mut f, "// ignore-tidy-filelength")?;
|
||||
writeln!(&mut f, "use super::windows_targets;")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ extern crate either;
|
|||
extern crate tracing;
|
||||
|
||||
// The rustc crates we need
|
||||
extern crate rustc_attr;
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_const_eval;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rand::rngs::StdRng;
|
|||
use rand::Rng;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use rustc_attr::InlineAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
#[allow(unused)]
|
||||
use rustc_data_structures::static_assert_size;
|
||||
|
|
@ -23,6 +24,7 @@ use rustc_middle::{
|
|||
Instance, Ty, TyCtxt,
|
||||
},
|
||||
};
|
||||
use rustc_session::config::InliningThreshold;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::{Span, SpanData, Symbol};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
|
@ -47,10 +49,10 @@ pub const SIGRTMIN: i32 = 34;
|
|||
/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
|
||||
pub const SIGRTMAX: i32 = 42;
|
||||
|
||||
/// Each const has multiple addresses, but only this many. Since const allocations are never
|
||||
/// deallocated, choosing a new [`AllocId`] and thus base address for each evaluation would
|
||||
/// produce unbounded memory usage.
|
||||
const ADDRS_PER_CONST: usize = 16;
|
||||
/// Each anonymous global (constant, vtable, function pointer, ...) has multiple addresses, but only
|
||||
/// this many. Since const allocations are never deallocated, choosing a new [`AllocId`] and thus
|
||||
/// base address for each evaluation would produce unbounded memory usage.
|
||||
const ADDRS_PER_ANON_GLOBAL: usize = 32;
|
||||
|
||||
/// Extra data stored with each stack frame
|
||||
pub struct FrameExtra<'tcx> {
|
||||
|
|
@ -1372,7 +1374,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
catch_unwind: None,
|
||||
timing,
|
||||
is_user_relevant: ecx.machine.is_user_relevant(&frame),
|
||||
salt: ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_CONST,
|
||||
salt: ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL,
|
||||
};
|
||||
|
||||
Ok(frame.with_extra(extra))
|
||||
|
|
@ -1518,4 +1520,45 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
Entry::Occupied(oe) => Ok(oe.get().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_global_alloc_salt(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
) -> usize {
|
||||
let unique = if let Some(instance) = instance {
|
||||
// Functions cannot be identified by pointers, as asm-equal functions can get
|
||||
// deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
|
||||
// functions can be duplicated across crates. We thus generate a new `AllocId` for every
|
||||
// mention of a function. This means that `main as fn() == main as fn()` is false, while
|
||||
// `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
|
||||
// be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
|
||||
// whether codegen will actually emit duplicate functions. It does that when they have
|
||||
// non-lifetime generics, or when they can be inlined. All other functions are given a
|
||||
// unique address. This is not a stable guarantee! The `inline` attribute is a hint and
|
||||
// cannot be relied upon for anything. But if we don't do this, the
|
||||
// `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
|
||||
// backtraces look terrible.
|
||||
let is_generic = instance
|
||||
.args
|
||||
.into_iter()
|
||||
.any(|kind| !matches!(kind.unpack(), ty::GenericArgKind::Lifetime(_)));
|
||||
let can_be_inlined = matches!(
|
||||
ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
|
||||
InliningThreshold::Always
|
||||
) || !matches!(
|
||||
ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
|
||||
InlineAttr::Never
|
||||
);
|
||||
!is_generic && !can_be_inlined
|
||||
} else {
|
||||
// Non-functions are never unique.
|
||||
false
|
||||
};
|
||||
// Always use the same salt if the allocation is unique.
|
||||
if unique {
|
||||
CTFE_ALLOC_SALT
|
||||
} else {
|
||||
ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,17 @@ fn unsized_dyn_autoderef() {
|
|||
}
|
||||
*/
|
||||
|
||||
fn vtable_ptr_eq() {
|
||||
use std::{fmt, ptr};
|
||||
|
||||
// We don't always get the same vtable when casting this to a wide pointer.
|
||||
let x = &2;
|
||||
let x_wide = x as &dyn fmt::Display;
|
||||
assert!((0..256).any(|_| !ptr::eq(x as &dyn fmt::Display, x_wide)));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
ref_box_dyn();
|
||||
box_box_trait();
|
||||
vtable_ptr_eq();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ fn main() {
|
|||
assert!(return_fn_ptr(i) == i);
|
||||
assert!(return_fn_ptr(i) as unsafe fn() -> i32 == i as fn() -> i32 as unsafe fn() -> i32);
|
||||
// Miri gives different addresses to different reifications of a generic function.
|
||||
assert!(return_fn_ptr(f) != f);
|
||||
// at least if we try often enough.
|
||||
assert!((0..256).any(|_| return_fn_ptr(f) != f));
|
||||
// However, if we only turn `f` into a function pointer and use that pointer,
|
||||
// it is equal to itself.
|
||||
let f2 = f as fn() -> i32;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ fn rc_fat_ptr_eq() {
|
|||
let p = Rc::new(1) as Rc<dyn Debug>;
|
||||
let a: *const dyn Debug = &*p;
|
||||
let r = Rc::into_raw(p);
|
||||
assert!(a == r);
|
||||
// Only compare the pointer parts, as the vtable might differ.
|
||||
assert!(a as *const () == r as *const ());
|
||||
drop(unsafe { Rc::from_raw(r) });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,20 @@ pub fn assert_not_contains_regex<H: AsRef<str>, N: AsRef<str>>(haystack: H, need
|
|||
}
|
||||
}
|
||||
|
||||
/// Assert that `haystack` contains `needle` a `count` number of times.
|
||||
#[track_caller]
|
||||
pub fn assert_count_is<H: AsRef<str>, N: AsRef<str>>(count: usize, haystack: H, needle: N) {
|
||||
let haystack = haystack.as_ref();
|
||||
let needle = needle.as_ref();
|
||||
if count != haystack.matches(needle).count() {
|
||||
eprintln!("=== HAYSTACK ===");
|
||||
eprintln!("{}", haystack);
|
||||
eprintln!("=== NEEDLE ===");
|
||||
eprintln!("{}", needle);
|
||||
panic!("needle did not appear {count} times in haystack");
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that all files in `dir1` exist and have the same content in `dir2`
|
||||
pub fn assert_dirs_are_equal(dir1: impl AsRef<Path>, dir2: impl AsRef<Path>) {
|
||||
let dir2 = dir2.as_ref();
|
||||
|
|
|
|||
|
|
@ -112,14 +112,8 @@ impl Diff {
|
|||
let (expected_name, actual_name, output, actual) = self.run_common();
|
||||
|
||||
if !output.is_empty() {
|
||||
// If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST`
|
||||
// environment variable set), then we write into the file and return.
|
||||
if let Some(ref expected_file) = self.expected_file {
|
||||
if std::env::var("RUSTC_BLESS_TEST").is_ok() {
|
||||
println!("Blessing `{}`", expected_file.display());
|
||||
fs::write(expected_file, actual);
|
||||
return;
|
||||
}
|
||||
if self.maybe_bless_expected_file(&actual) {
|
||||
return;
|
||||
}
|
||||
panic!(
|
||||
"test failed: `{}` is different from `{}`\n\n{}",
|
||||
|
|
@ -134,14 +128,8 @@ impl Diff {
|
|||
let (expected_name, actual_name, output, actual) = self.run_common();
|
||||
|
||||
if output.is_empty() {
|
||||
// If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST`
|
||||
// environment variable set), then we write into the file and return.
|
||||
if let Some(ref expected_file) = self.expected_file {
|
||||
if std::env::var("RUSTC_BLESS_TEST").is_ok() {
|
||||
println!("Blessing `{}`", expected_file.display());
|
||||
fs::write(expected_file, actual);
|
||||
return;
|
||||
}
|
||||
if self.maybe_bless_expected_file(&actual) {
|
||||
return;
|
||||
}
|
||||
panic!(
|
||||
"test failed: `{}` is not different from `{}`\n\n{}",
|
||||
|
|
@ -149,4 +137,24 @@ impl Diff {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// If we have an expected file to write into, and `RUSTC_BLESS_TEST` is
|
||||
/// set, then write the actual output into the file and return `true`.
|
||||
///
|
||||
/// We assume that `RUSTC_BLESS_TEST` contains the path to the original test's
|
||||
/// source directory. That lets us bless the original snapshot file in the
|
||||
/// source tree, not the copy in `rmake_out` that we would normally use.
|
||||
fn maybe_bless_expected_file(&self, actual: &str) -> bool {
|
||||
let Some(ref expected_file) = self.expected_file else {
|
||||
return false;
|
||||
};
|
||||
let Ok(bless_dir) = std::env::var("RUSTC_BLESS_TEST") else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let bless_file = Path::new(&bless_dir).join(expected_file);
|
||||
println!("Blessing `{}`", bless_file.display());
|
||||
fs::write(bless_file, actual);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@ pub fn llvm_bcanalyzer() -> LlvmBcanalyzer {
|
|||
LlvmBcanalyzer::new()
|
||||
}
|
||||
|
||||
/// Construct a new `llvm-dwarfdump` invocation. This assumes that `llvm-dwarfdump` is available
|
||||
/// at `$LLVM_BIN_DIR/llvm-dwarfdump`.
|
||||
pub fn llvm_dwarfdump() -> LlvmDwarfdump {
|
||||
LlvmDwarfdump::new()
|
||||
}
|
||||
|
||||
/// A `llvm-readobj` invocation builder.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
|
|
@ -97,6 +103,13 @@ pub struct LlvmBcanalyzer {
|
|||
cmd: Command,
|
||||
}
|
||||
|
||||
/// A `llvm-dwarfdump` invocation builder.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct LlvmDwarfdump {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
crate::macros::impl_common_helpers!(LlvmReadobj);
|
||||
crate::macros::impl_common_helpers!(LlvmProfdata);
|
||||
crate::macros::impl_common_helpers!(LlvmFilecheck);
|
||||
|
|
@ -104,6 +117,7 @@ crate::macros::impl_common_helpers!(LlvmObjdump);
|
|||
crate::macros::impl_common_helpers!(LlvmAr);
|
||||
crate::macros::impl_common_helpers!(LlvmNm);
|
||||
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
|
||||
crate::macros::impl_common_helpers!(LlvmDwarfdump);
|
||||
|
||||
/// Generate the path to the bin directory of LLVM.
|
||||
#[must_use]
|
||||
|
|
@ -317,3 +331,19 @@ impl LlvmBcanalyzer {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LlvmDwarfdump {
|
||||
/// Construct a new `llvm-dwarfdump` invocation. This assumes that `llvm-dwarfdump` is available
|
||||
/// at `$LLVM_BIN_DIR/llvm-dwarfdump`.
|
||||
pub fn new() -> Self {
|
||||
let llvm_dwarfdump = llvm_bin_dir().join("llvm-dwarfdump");
|
||||
let cmd = Command::new(llvm_dwarfdump);
|
||||
Self { cmd }
|
||||
}
|
||||
|
||||
/// Provide an input file.
|
||||
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
self.cmd.arg(path.as_ref());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,9 @@ pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_nativ
|
|||
pub use clang::{clang, Clang};
|
||||
pub use htmldocck::htmldocck;
|
||||
pub use llvm::{
|
||||
llvm_ar, llvm_bcanalyzer, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj,
|
||||
LlvmAr, LlvmBcanalyzer, LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj,
|
||||
llvm_ar, llvm_bcanalyzer, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata,
|
||||
llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjdump,
|
||||
LlvmProfdata, LlvmReadobj,
|
||||
};
|
||||
pub use python::python_command;
|
||||
pub use rustc::{aux_build, bare_rustc, rustc, Rustc};
|
||||
|
|
@ -86,7 +87,7 @@ pub use path_helpers::{
|
|||
pub use scoped_run::{run_in_tmpdir, test_while_readonly};
|
||||
|
||||
pub use assertion_helpers::{
|
||||
assert_contains, assert_contains_regex, assert_dirs_are_equal, assert_equals,
|
||||
assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals,
|
||||
assert_not_contains, assert_not_contains_regex,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ jobs:
|
|||
run: |
|
||||
git config --global user.email "runner@gha.local"
|
||||
git config --global user.name "GitHub Action"
|
||||
# Remove r-a crates from the workspaces so we don't auto-publish them as well
|
||||
sed -i 's/ "crates\/\*"//' ./Cargo.toml
|
||||
sed -i 's/ "xtask\/"//' ./Cargo.toml
|
||||
# Only publish the crates under lib/
|
||||
sed -i 's|^members = .*$|members = ["lib/*"]|' Cargo.toml
|
||||
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty
|
||||
|
|
|
|||
|
|
@ -52,16 +52,16 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.72"
|
||||
version = "0.3.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object 0.35.0",
|
||||
"object 0.36.3",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
|
|
@ -92,9 +92,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
|
@ -136,9 +136,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.98"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
|
||||
|
||||
[[package]]
|
||||
name = "cfg"
|
||||
|
|
@ -148,10 +148,10 @@ dependencies = [
|
|||
"derive_arbitrary",
|
||||
"expect-test",
|
||||
"intern",
|
||||
"mbe",
|
||||
"oorandom",
|
||||
"rustc-hash",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"tt",
|
||||
]
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ version = "0.98.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"chalk-derive",
|
||||
]
|
||||
|
||||
|
|
@ -226,9 +226,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
|||
|
||||
[[package]]
|
||||
name = "cov-mark"
|
||||
version = "2.0.0-pre.1"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a"
|
||||
checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
|
|
@ -366,9 +366,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
|
|
@ -397,14 +397,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.23"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"windows-sys 0.52.0",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -415,31 +415,14 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
|||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
||||
checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flycheck"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cargo_metadata",
|
||||
"crossbeam-channel",
|
||||
"paths",
|
||||
"process-wrap",
|
||||
"project-model",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"stdx",
|
||||
"toolchain",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
|
|
@ -529,7 +512,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"arrayvec",
|
||||
"base-db",
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"cfg",
|
||||
"cov-mark",
|
||||
"dashmap",
|
||||
|
|
@ -554,6 +537,7 @@ dependencies = [
|
|||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"test-fixture",
|
||||
"test-utils",
|
||||
"tracing",
|
||||
|
|
@ -582,6 +566,7 @@ dependencies = [
|
|||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"tracing",
|
||||
"triomphe",
|
||||
"tt",
|
||||
|
|
@ -593,7 +578,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"arrayvec",
|
||||
"base-db",
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"chalk-derive",
|
||||
"chalk-ir",
|
||||
"chalk-recursive",
|
||||
|
|
@ -722,7 +707,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"arrayvec",
|
||||
"base-db",
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"cov-mark",
|
||||
"crossbeam-channel",
|
||||
"either",
|
||||
|
|
@ -803,9 +788,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
|
|
@ -895,9 +880,9 @@ checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306"
|
|||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
|
|
@ -907,19 +892,19 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.38"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
|
||||
checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
@ -931,8 +916,9 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -996,9 +982,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "lsp-server"
|
||||
|
|
@ -1056,6 +1042,7 @@ dependencies = [
|
|||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"test-utils",
|
||||
"tracing",
|
||||
"tt",
|
||||
|
|
@ -1063,9 +1050,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
|
|
@ -1087,18 +1074,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.42"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
|
||||
checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
|
@ -1130,7 +1117,7 @@ version = "0.28.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
|
|
@ -1148,7 +1135,7 @@ version = "6.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"crossbeam-channel",
|
||||
"filetime",
|
||||
"fsevent-sys",
|
||||
|
|
@ -1163,11 +1150,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.0"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1197,9 +1184,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.35.0"
|
||||
version = "0.36.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
||||
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -1212,9 +1199,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
version = "11.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
|
|
@ -1240,9 +1227,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.1",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1268,6 +1255,7 @@ name = "paths"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1319,9 +1307,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-api"
|
||||
|
|
@ -1330,14 +1321,12 @@ dependencies = [
|
|||
"base-db",
|
||||
"indexmap",
|
||||
"intern",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paths",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"span",
|
||||
"stdx",
|
||||
"text-size",
|
||||
"tracing",
|
||||
"tt",
|
||||
]
|
||||
|
|
@ -1350,7 +1339,6 @@ dependencies = [
|
|||
"expect-test",
|
||||
"intern",
|
||||
"libloading",
|
||||
"mbe",
|
||||
"memmap2",
|
||||
"object 0.33.0",
|
||||
"paths",
|
||||
|
|
@ -1360,6 +1348,7 @@ dependencies = [
|
|||
"snap",
|
||||
"span",
|
||||
"stdx",
|
||||
"syntax-bridge",
|
||||
"tt",
|
||||
]
|
||||
|
||||
|
|
@ -1380,9 +1369,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -1460,7 +1449,7 @@ version = "0.9.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
|
@ -1485,20 +1474,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_abi"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
|
||||
checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"ra-ap-rustc_index",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
|
||||
checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"ra-ap-rustc_index_macros",
|
||||
|
|
@ -1507,21 +1496,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index_macros"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
|
||||
checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
|
||||
checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d"
|
||||
dependencies = [
|
||||
"unicode-properties",
|
||||
"unicode-xid",
|
||||
|
|
@ -1529,9 +1517,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_parse_format"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
|
||||
checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"ra-ap-rustc_lexer",
|
||||
|
|
@ -1539,9 +1527,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_pattern_analysis"
|
||||
version = "0.53.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
|
||||
checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"rustc-hash",
|
||||
|
|
@ -1602,20 +1590,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1648,12 +1627,12 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"always-assert",
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"cfg",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dissimilar",
|
||||
"expect-test",
|
||||
"flycheck",
|
||||
"hir",
|
||||
"hir-def",
|
||||
"hir-ty",
|
||||
|
|
@ -1665,7 +1644,6 @@ dependencies = [
|
|||
"load-cargo",
|
||||
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lsp-types",
|
||||
"mbe",
|
||||
"memchr",
|
||||
"mimalloc",
|
||||
"nohash-hasher",
|
||||
|
|
@ -1675,6 +1653,7 @@ dependencies = [
|
|||
"parser",
|
||||
"paths",
|
||||
"proc-macro-api",
|
||||
"process-wrap",
|
||||
"profile",
|
||||
"project-model",
|
||||
"rayon",
|
||||
|
|
@ -1685,6 +1664,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"test-fixture",
|
||||
"test-utils",
|
||||
"tikv-jemallocator",
|
||||
|
|
@ -1716,9 +1696,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustc_apfloat"
|
||||
version = "0.2.0+llvm-462a31f5a5ab"
|
||||
version = "0.2.1+llvm-462a31f5a5ab"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be"
|
||||
checksum = "886d94c63c812a8037c4faca2607453a0fa4cf82f734665266876b022244543f"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"smallvec",
|
||||
|
|
@ -1801,18 +1781,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.206"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.206"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1821,12 +1801,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -1844,9 +1825,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -1923,9 +1904,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
version = "2.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1967,6 +1948,21 @@ dependencies = [
|
|||
"triomphe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntax-bridge"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"intern",
|
||||
"parser",
|
||||
"rustc-hash",
|
||||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"test-utils",
|
||||
"tracing",
|
||||
"tt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-fixture"
|
||||
version = "0.0.0"
|
||||
|
|
@ -1987,6 +1983,7 @@ name = "test-utils"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"paths",
|
||||
"profile",
|
||||
"rustc-hash",
|
||||
"stdx",
|
||||
|
|
@ -2010,18 +2007,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2090,9 +2087,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
|
@ -2105,9 +2102,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.14"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
|
@ -2117,18 +2114,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.6"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.14"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
|
@ -2214,9 +2211,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90"
|
||||
checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
|
|
@ -2289,9 +2286,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
|
@ -2307,14 +2304,15 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
|||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vfs"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"fst",
|
||||
"indexmap",
|
||||
"nohash-hasher",
|
||||
|
|
@ -2331,6 +2329,8 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"notify",
|
||||
"paths",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"stdx",
|
||||
"tracing",
|
||||
"vfs",
|
||||
|
|
@ -2355,11 +2355,11 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2369,7 +2369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2381,7 +2381,7 @@ dependencies = [
|
|||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2408,11 +2408,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2430,7 +2430,16 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2450,18 +2459,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2472,9 +2481,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
|
@ -2484,9 +2493,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
|
@ -2496,15 +2505,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
|
@ -2514,9 +2523,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
|
@ -2526,9 +2535,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
|
|
@ -2538,9 +2547,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
|
@ -2550,15 +2559,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.11"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -2618,6 +2627,27 @@ dependencies = [
|
|||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.6"
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.78"
|
||||
rust-version = "1.80"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
repository = "https://github.com/rust-lang/rust-analyzer"
|
||||
|
||||
[profile.dev]
|
||||
debug = 1
|
||||
|
|
@ -51,7 +52,6 @@ debug = 2
|
|||
# local crates
|
||||
base-db = { path = "./crates/base-db", version = "0.0.0" }
|
||||
cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
|
||||
flycheck = { path = "./crates/flycheck", version = "0.0.0" }
|
||||
hir = { path = "./crates/hir", version = "0.0.0" }
|
||||
hir-def = { path = "./crates/hir-def", version = "0.0.0" }
|
||||
hir-expand = { path = "./crates/hir-expand", version = "0.0.0" }
|
||||
|
|
@ -77,17 +77,18 @@ salsa = { path = "./crates/salsa", version = "0.0.0" }
|
|||
span = { path = "./crates/span", version = "0.0.0" }
|
||||
stdx = { path = "./crates/stdx", version = "0.0.0" }
|
||||
syntax = { path = "./crates/syntax", version = "0.0.0" }
|
||||
syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" }
|
||||
text-edit = { path = "./crates/text-edit", version = "0.0.0" }
|
||||
toolchain = { path = "./crates/toolchain", version = "0.0.0" }
|
||||
tt = { path = "./crates/tt", version = "0.0.0" }
|
||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
|
||||
ra-ap-rustc_lexer = { version = "0.53.0", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.53.0", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.53.0", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.53.0", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.63.0", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.63.0", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.63.0", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
test-fixture = { path = "./crates/test-fixture" }
|
||||
|
|
@ -124,11 +125,11 @@ memmap2 = "0.5.4"
|
|||
nohash-hasher = "0.2.0"
|
||||
oorandom = "11.1.3"
|
||||
object = { version = "0.33.0", default-features = false, features = [
|
||||
"std",
|
||||
"read_core",
|
||||
"elf",
|
||||
"macho",
|
||||
"pe",
|
||||
"std",
|
||||
"read_core",
|
||||
"elf",
|
||||
"macho",
|
||||
"pe",
|
||||
] }
|
||||
process-wrap = { version = "8.0.2", features = ["std"] }
|
||||
pulldown-cmark-to-cmark = "10.0.4"
|
||||
|
|
@ -158,7 +159,6 @@ url = "2.3.1"
|
|||
xshell = "0.2.5"
|
||||
|
||||
|
||||
|
||||
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
||||
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "base-db"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
repository.workspace = true
|
||||
description = "Basic database traits for rust-analyzer. The concrete DB is defined by `ide` (aka `ra_ap_ide`)."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use salsa::Durability;
|
|||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId};
|
||||
use crate::{CrateGraph, SourceDatabaseFileInputExt, SourceRoot, SourceRootDatabase, SourceRootId};
|
||||
|
||||
/// Encapsulate a bunch of raw `.set` calls on the database.
|
||||
#[derive(Default)]
|
||||
|
|
@ -50,7 +50,7 @@ impl FileChange {
|
|||
self.crate_graph = Some(graph);
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
|
||||
pub fn apply(self, db: &mut dyn SourceRootDatabase) {
|
||||
let _p = tracing::info_span!("FileChange::apply").entered();
|
||||
if let Some(roots) = self.roots {
|
||||
for (idx, root) in roots.into_iter().enumerate() {
|
||||
|
|
|
|||
|
|
@ -690,6 +690,14 @@ impl Env {
|
|||
pub fn extend_from_other(&mut self, other: &Env) {
|
||||
self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, k: impl Into<String>, v: impl Into<String>) -> Option<String> {
|
||||
self.entries.insert(k.into(), v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Env> for Vec<(String, String)> {
|
||||
|
|
@ -700,6 +708,15 @@ impl From<Env> for Vec<(String, String)> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Env {
|
||||
type Item = (&'a String, &'a String);
|
||||
type IntoIter = std::collections::hash_map::Iter<'a, String, String>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.entries.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CyclicDependenciesError {
|
||||
path: Vec<(CrateId, Option<CrateDisplayName>)>,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! base_db defines basic database traits. The concrete DB is defined by ide.
|
||||
|
||||
// FIXME: Rename this crate, base db is non descriptive
|
||||
mod change;
|
||||
mod input;
|
||||
|
||||
|
|
@ -47,8 +47,6 @@ pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
|
|||
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;
|
||||
|
||||
pub trait FileLoader {
|
||||
/// Text of the file.
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
|
||||
/// Crates whose root's source root is the same as the source root of `file_id`
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
|
||||
|
|
@ -58,6 +56,13 @@ pub trait FileLoader {
|
|||
/// model. Everything else in rust-analyzer is derived from these queries.
|
||||
#[salsa::query_group(SourceDatabaseStorage)]
|
||||
pub trait SourceDatabase: FileLoader + std::fmt::Debug {
|
||||
#[salsa::input]
|
||||
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
|
||||
|
||||
/// Text of the file.
|
||||
#[salsa::lru]
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
|
||||
/// Parses the file into the syntax tree.
|
||||
#[salsa::lru]
|
||||
fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
|
||||
|
|
@ -99,16 +104,18 @@ fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option<Arc
|
|||
}
|
||||
}
|
||||
|
||||
fn file_text(db: &dyn SourceDatabase, file_id: FileId) -> Arc<str> {
|
||||
let bytes = db.compressed_file_text(file_id);
|
||||
let bytes =
|
||||
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
|
||||
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
|
||||
Arc::from(text)
|
||||
}
|
||||
|
||||
/// We don't want to give HIR knowledge of source roots, hence we extract these
|
||||
/// methods into a separate DB.
|
||||
#[salsa::query_group(SourceDatabaseExtStorage)]
|
||||
pub trait SourceDatabaseExt: SourceDatabase {
|
||||
#[salsa::input]
|
||||
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
|
||||
|
||||
#[salsa::lru]
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
|
||||
#[salsa::query_group(SourceRootDatabaseStorage)]
|
||||
pub trait SourceRootDatabase: SourceDatabase {
|
||||
/// Path to a file, relative to the root of its source root.
|
||||
/// Source root of the file.
|
||||
#[salsa::input]
|
||||
|
|
@ -121,15 +128,7 @@ pub trait SourceDatabaseExt: SourceDatabase {
|
|||
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
|
||||
}
|
||||
|
||||
fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc<str> {
|
||||
let bytes = db.compressed_file_text(file_id);
|
||||
let bytes =
|
||||
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
|
||||
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
|
||||
Arc::from(text)
|
||||
}
|
||||
|
||||
pub trait SourceDatabaseExt2 {
|
||||
pub trait SourceDatabaseFileInputExt {
|
||||
fn set_file_text(&mut self, file_id: FileId, text: &str) {
|
||||
self.set_file_text_with_durability(file_id, text, Durability::LOW);
|
||||
}
|
||||
|
|
@ -142,7 +141,7 @@ pub trait SourceDatabaseExt2 {
|
|||
);
|
||||
}
|
||||
|
||||
impl<Db: ?Sized + SourceDatabaseExt> SourceDatabaseExt2 for Db {
|
||||
impl<Db: ?Sized + SourceRootDatabase> SourceDatabaseFileInputExt for Db {
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: FileId,
|
||||
|
|
@ -159,7 +158,7 @@ impl<Db: ?Sized + SourceDatabaseExt> SourceDatabaseExt2 for Db {
|
|||
}
|
||||
}
|
||||
|
||||
fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> {
|
||||
fn source_root_crates(db: &dyn SourceRootDatabase, id: SourceRootId) -> Arc<[CrateId]> {
|
||||
let graph = db.crate_graph();
|
||||
let mut crates = graph
|
||||
.iter()
|
||||
|
|
@ -173,13 +172,12 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[Crat
|
|||
crates.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Silly workaround for cyclic deps between the traits
|
||||
// FIXME: Would be nice to get rid of this somehow
|
||||
/// Silly workaround for cyclic deps due to the SourceRootDatabase and SourceDatabase split
|
||||
/// regarding FileLoader
|
||||
pub struct FileLoaderDelegate<T>(pub T);
|
||||
|
||||
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str> {
|
||||
SourceDatabaseExt::file_text(self.0, file_id)
|
||||
}
|
||||
impl<T: SourceRootDatabase> FileLoader for FileLoaderDelegate<&'_ T> {
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
// FIXME: this *somehow* should be platform agnostic...
|
||||
let source_root = self.0.file_source_root(path.anchor);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "cfg"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
repository.workspace = true
|
||||
description = "Conditional compiling options, `cfg` attribute parser and evaluator for rust-analyzer."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
@ -28,7 +29,7 @@ arbitrary = "1.3.2"
|
|||
derive_arbitrary = "1.3.2"
|
||||
|
||||
# local deps
|
||||
mbe.workspace = true
|
||||
syntax-bridge.workspace = true
|
||||
syntax.workspace = true
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -108,6 +108,14 @@ impl<'a> IntoIterator for &'a CfgOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromIterator<CfgAtom> for CfgOptions {
|
||||
fn from_iter<T: IntoIterator<Item = CfgAtom>>(iter: T) -> Self {
|
||||
let mut options = CfgOptions::default();
|
||||
options.extend(iter);
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CfgDiff {
|
||||
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use expect_test::{expect, Expect};
|
||||
use intern::Symbol;
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY};
|
||||
use syntax::{ast, AstNode, Edition};
|
||||
use syntax_bridge::{
|
||||
dummy_test_span_utils::{DummyTestSpanMap, DUMMY},
|
||||
syntax_node_to_token_tree, DocCommentDesugarMode,
|
||||
};
|
||||
|
||||
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
[package]
|
||||
name = "flycheck"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
cargo_metadata.workspace = true
|
||||
crossbeam-channel.workspace = true
|
||||
tracing.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
process-wrap.workspace = true
|
||||
|
||||
# local deps
|
||||
paths.workspace = true
|
||||
stdx.workspace = true
|
||||
toolchain.workspace = true
|
||||
project-model.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "hir-def"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
repository.workspace = true
|
||||
description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
@ -52,7 +53,7 @@ expect-test.workspace = true
|
|||
# local deps
|
||||
test-utils.workspace = true
|
||||
test-fixture.workspace = true
|
||||
|
||||
syntax-bridge.workspace = true
|
||||
[features]
|
||||
in-rust-tree = ["hir-expand/in-rust-tree"]
|
||||
|
||||
|
|
|
|||
|
|
@ -657,9 +657,9 @@ mod tests {
|
|||
use triomphe::Arc;
|
||||
|
||||
use hir_expand::span_map::{RealSpanMap, SpanMap};
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use span::FileId;
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ pub enum BodyDiagnostic {
|
|||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
|
||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
||||
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
|
||||
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ impl Body {
|
|||
}),
|
||||
)
|
||||
});
|
||||
is_async_fn = data.has_async_kw();
|
||||
is_async_fn = data.is_async();
|
||||
src.map(|it| it.body().map(ast::Expr::from))
|
||||
}
|
||||
DefWithBodyId::ConstId(c) => {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ pub(super) fn lower(
|
|||
is_lowering_coroutine: false,
|
||||
label_ribs: Vec::new(),
|
||||
current_binding_owner: None,
|
||||
awaitable_context: None,
|
||||
}
|
||||
.collect(params, body, is_async_fn)
|
||||
}
|
||||
|
|
@ -100,6 +101,8 @@ struct ExprCollector<'a> {
|
|||
// resolution
|
||||
label_ribs: Vec<LabelRib>,
|
||||
current_binding_owner: Option<ExprId>,
|
||||
|
||||
awaitable_context: Option<Awaitable>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -135,6 +138,11 @@ impl RibKind {
|
|||
}
|
||||
}
|
||||
|
||||
enum Awaitable {
|
||||
Yes,
|
||||
No(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct BindingList {
|
||||
map: FxHashMap<Name, BindingId>,
|
||||
|
|
@ -180,6 +188,18 @@ impl ExprCollector<'_> {
|
|||
body: Option<ast::Expr>,
|
||||
is_async_fn: bool,
|
||||
) -> (Body, BodySourceMap) {
|
||||
self.awaitable_context.replace(if is_async_fn {
|
||||
Awaitable::Yes
|
||||
} else {
|
||||
match self.owner {
|
||||
DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
|
||||
DefWithBodyId::StaticId(..) => Awaitable::No("static"),
|
||||
DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
|
||||
Awaitable::No("constant")
|
||||
}
|
||||
DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
|
||||
}
|
||||
});
|
||||
if let Some((param_list, mut attr_enabled)) = param_list {
|
||||
let mut params = vec![];
|
||||
if let Some(self_param) =
|
||||
|
|
@ -280,31 +300,40 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
Some(ast::BlockModifier::Async(_)) => {
|
||||
self.with_label_rib(RibKind::Closure, |this| {
|
||||
this.collect_block_(e, |id, statements, tail| Expr::Async {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
this.with_awaitable_block(Awaitable::Yes, |this| {
|
||||
this.collect_block_(e, |id, statements, tail| Expr::Async {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Const(_)) => {
|
||||
self.with_label_rib(RibKind::Constant, |this| {
|
||||
let (result_expr_id, prev_binding_owner) =
|
||||
this.initialize_binding_owner(syntax_ptr);
|
||||
let inner_expr = this.collect_block(e);
|
||||
let it = this.db.intern_anonymous_const(ConstBlockLoc {
|
||||
parent: this.owner,
|
||||
root: inner_expr,
|
||||
});
|
||||
this.body.exprs[result_expr_id] = Expr::Const(it);
|
||||
this.current_binding_owner = prev_binding_owner;
|
||||
result_expr_id
|
||||
this.with_awaitable_block(Awaitable::No("constant block"), |this| {
|
||||
let (result_expr_id, prev_binding_owner) =
|
||||
this.initialize_binding_owner(syntax_ptr);
|
||||
let inner_expr = this.collect_block(e);
|
||||
let it = this.db.intern_anonymous_const(ConstBlockLoc {
|
||||
parent: this.owner,
|
||||
root: inner_expr,
|
||||
});
|
||||
this.body.exprs[result_expr_id] = Expr::Const(it);
|
||||
this.current_binding_owner = prev_binding_owner;
|
||||
result_expr_id
|
||||
})
|
||||
})
|
||||
}
|
||||
// FIXME
|
||||
Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => {
|
||||
self.collect_block(e)
|
||||
Some(ast::BlockModifier::AsyncGen(_)) => {
|
||||
self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e))
|
||||
}
|
||||
Some(ast::BlockModifier::Gen(_)) => self
|
||||
.with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
|
||||
this.collect_block(e)
|
||||
}),
|
||||
None => self.collect_block(e),
|
||||
},
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
|
|
@ -469,6 +498,12 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::AwaitExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync {
|
||||
node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
|
||||
location: location.to_string(),
|
||||
});
|
||||
}
|
||||
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
|
||||
|
|
@ -527,7 +562,13 @@ impl ExprCollector<'_> {
|
|||
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
|
||||
let prev_try_block_label = this.current_try_block_label.take();
|
||||
|
||||
let body = this.collect_expr_opt(e.body());
|
||||
let awaitable = if e.async_token().is_some() {
|
||||
Awaitable::Yes
|
||||
} else {
|
||||
Awaitable::No("non-async closure")
|
||||
};
|
||||
let body =
|
||||
this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
|
||||
|
||||
let closure_kind = if this.is_lowering_coroutine {
|
||||
let movability = if e.static_token().is_some() {
|
||||
|
|
@ -2082,6 +2123,21 @@ impl ExprCollector<'_> {
|
|||
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
||||
self.body.labels.alloc(label)
|
||||
}
|
||||
|
||||
fn is_lowering_awaitable_block(&self) -> &Awaitable {
|
||||
self.awaitable_context.as_ref().unwrap_or(&Awaitable::No("unknown"))
|
||||
}
|
||||
|
||||
fn with_awaitable_block<T>(
|
||||
&mut self,
|
||||
awaitable: Awaitable,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> T {
|
||||
let orig = self.awaitable_context.replace(awaitable);
|
||||
let res = f(self);
|
||||
self.awaitable_context = orig;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ impl FunctionData {
|
|||
.filter(|it| !it.is_empty())
|
||||
.map(Box::new);
|
||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||
if flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||
&& !crate_graph[krate].edition.at_least_2024()
|
||||
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
|
||||
{
|
||||
flags.remove(FnFlags::HAS_UNSAFE_KW);
|
||||
}
|
||||
|
||||
Arc::new(FunctionData {
|
||||
name: func.name.clone(),
|
||||
|
|
@ -126,19 +132,19 @@ impl FunctionData {
|
|||
self.flags.contains(FnFlags::HAS_SELF_PARAM)
|
||||
}
|
||||
|
||||
pub fn has_default_kw(&self) -> bool {
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_DEFAULT_KW)
|
||||
}
|
||||
|
||||
pub fn has_const_kw(&self) -> bool {
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_CONST_KW)
|
||||
}
|
||||
|
||||
pub fn has_async_kw(&self) -> bool {
|
||||
pub fn is_async(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_ASYNC_KW)
|
||||
}
|
||||
|
||||
pub fn has_unsafe_kw(&self) -> bool {
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
|
||||
|
||||
#[salsa::invoke(StaticData::static_data_query)]
|
||||
fn static_data(&self, konst: StaticId) -> Arc<StaticData>;
|
||||
fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
|
||||
|
||||
#[salsa::invoke(Macro2Data::macro2_data_query)]
|
||||
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
|
||||
|
|
@ -240,14 +240,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
|
||||
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||
|
||||
fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, EditionedFileId)>;
|
||||
fn include_macro_invoc(&self, crate_id: CrateId) -> Arc<[(MacroCallId, EditionedFileId)]>;
|
||||
}
|
||||
|
||||
// return: macro call id and include file id
|
||||
fn include_macro_invoc(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> Vec<(MacroCallId, EditionedFileId)> {
|
||||
) -> Arc<[(MacroCallId, EditionedFileId)]> {
|
||||
db.crate_def_map(krate)
|
||||
.modules
|
||||
.values()
|
||||
|
|
|
|||
|
|
@ -50,13 +50,13 @@ pub fn find_path(
|
|||
prefix: prefix_kind,
|
||||
cfg,
|
||||
ignore_local_imports,
|
||||
is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
|
||||
from,
|
||||
from_def_map: &from.def_map(db),
|
||||
fuel: Cell::new(FIND_PATH_FUEL),
|
||||
},
|
||||
item,
|
||||
MAX_PATH_LEN,
|
||||
db.crate_graph()[item_module.krate()].origin.is_lang(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -98,20 +98,16 @@ struct FindPathCtx<'db> {
|
|||
prefix: PrefixKind,
|
||||
cfg: ImportPathConfig,
|
||||
ignore_local_imports: bool,
|
||||
is_std_item: bool,
|
||||
from: ModuleId,
|
||||
from_def_map: &'db DefMap,
|
||||
fuel: Cell<usize>,
|
||||
}
|
||||
|
||||
/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
|
||||
fn find_path_inner(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
is_std_item: bool,
|
||||
) -> Option<ModPath> {
|
||||
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
|
||||
// - if the item is a module, jump straight to module search
|
||||
if !is_std_item {
|
||||
if !ctx.is_std_item {
|
||||
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
|
||||
return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len)
|
||||
.map(|choice| choice.path);
|
||||
|
|
@ -138,12 +134,9 @@ fn find_path_inner(
|
|||
|
||||
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
||||
// - if the item is an enum variant, refer to it via the enum
|
||||
if let Some(mut path) = find_path_inner(
|
||||
ctx,
|
||||
ItemInNs::Types(variant.lookup(ctx.db).parent.into()),
|
||||
max_len,
|
||||
is_std_item,
|
||||
) {
|
||||
if let Some(mut path) =
|
||||
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
|
||||
{
|
||||
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
|
||||
return Some(path);
|
||||
}
|
||||
|
|
@ -152,16 +145,6 @@ fn find_path_inner(
|
|||
// variant somewhere
|
||||
}
|
||||
|
||||
if is_std_item {
|
||||
// The item we are searching for comes from the sysroot libraries, so skip prefer looking in
|
||||
// the sysroot libraries directly.
|
||||
// We do need to fallback as the item in question could be re-exported by another crate
|
||||
// while not being a transitive dependency of the current crate.
|
||||
if let Some(choice) = find_in_sysroot(ctx, &mut FxHashSet::default(), item, max_len) {
|
||||
return Some(choice.path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut best_choice = None;
|
||||
calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice);
|
||||
best_choice.map(|choice| choice.path)
|
||||
|
|
@ -366,6 +349,12 @@ fn calculate_best_path(
|
|||
// Item was defined in the same crate that wants to import it. It cannot be found in any
|
||||
// dependency in this case.
|
||||
calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice)
|
||||
} else if ctx.is_std_item {
|
||||
// The item we are searching for comes from the sysroot libraries, so skip prefer looking in
|
||||
// the sysroot libraries directly.
|
||||
// We do need to fallback as the item in question could be re-exported by another crate
|
||||
// while not being a transitive dependency of the current crate.
|
||||
find_in_sysroot(ctx, visited_modules, item, max_len, best_choice)
|
||||
} else {
|
||||
// Item was defined in some upstream crate. This means that it must be exported from one,
|
||||
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
|
||||
|
|
@ -382,10 +371,10 @@ fn find_in_sysroot(
|
|||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
) -> Option<Choice> {
|
||||
best_choice: &mut Option<Choice>,
|
||||
) {
|
||||
let crate_graph = ctx.db.crate_graph();
|
||||
let dependencies = &crate_graph[ctx.from.krate].dependencies;
|
||||
let mut best_choice = None;
|
||||
let mut search = |lang, best_choice: &mut _| {
|
||||
if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
|
||||
match crate_graph[dep.crate_id].origin {
|
||||
|
|
@ -397,29 +386,31 @@ fn find_in_sysroot(
|
|||
}
|
||||
};
|
||||
if ctx.cfg.prefer_no_std {
|
||||
search(LangCrateOrigin::Core, &mut best_choice);
|
||||
search(LangCrateOrigin::Core, best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
return;
|
||||
}
|
||||
search(LangCrateOrigin::Std, &mut best_choice);
|
||||
search(LangCrateOrigin::Std, best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
search(LangCrateOrigin::Std, &mut best_choice);
|
||||
search(LangCrateOrigin::Std, best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
return;
|
||||
}
|
||||
search(LangCrateOrigin::Core, &mut best_choice);
|
||||
search(LangCrateOrigin::Core, best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut best_choice = None;
|
||||
dependencies.iter().filter(|it| it.is_sysroot()).for_each(|dep| {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, &mut best_choice, dep.crate_id);
|
||||
});
|
||||
best_choice
|
||||
dependencies
|
||||
.iter()
|
||||
.filter(|it| it.is_sysroot())
|
||||
.chain(dependencies.iter().filter(|it| !it.is_sysroot()))
|
||||
.for_each(|dep| {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
|
||||
});
|
||||
}
|
||||
|
||||
fn find_in_dep(
|
||||
|
|
@ -491,6 +482,7 @@ fn calculate_best_path_local(
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Choice {
|
||||
path: ModPath,
|
||||
/// The length in characters of the path
|
||||
|
|
@ -676,6 +668,7 @@ mod tests {
|
|||
path: &str,
|
||||
prefer_prelude: bool,
|
||||
prefer_absolute: bool,
|
||||
prefer_no_std: bool,
|
||||
expect: Expect,
|
||||
) {
|
||||
let (db, pos) = TestDB::with_position(ra_fixture);
|
||||
|
|
@ -717,7 +710,7 @@ mod tests {
|
|||
module,
|
||||
prefix,
|
||||
ignore_local_imports,
|
||||
ImportPathConfig { prefer_no_std: false, prefer_prelude, prefer_absolute },
|
||||
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
|
||||
);
|
||||
format_to!(
|
||||
res,
|
||||
|
|
@ -732,15 +725,19 @@ mod tests {
|
|||
}
|
||||
|
||||
fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
|
||||
check_found_path_(ra_fixture, path, false, false, expect);
|
||||
check_found_path_(ra_fixture, path, false, false, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
|
||||
check_found_path_(ra_fixture, path, true, false, expect);
|
||||
check_found_path_(ra_fixture, path, true, false, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
|
||||
check_found_path_(ra_fixture, path, false, true, expect);
|
||||
check_found_path_(ra_fixture, path, false, true, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
|
||||
check_found_path_(ra_fixture, path, false, false, true, expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1361,9 +1358,66 @@ pub mod sync {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn prefer_core_paths_over_std_for_mod_reexport() {
|
||||
check_found_path_prefer_no_std(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
|
||||
$0
|
||||
|
||||
//- /stdlib.rs crate:std deps:core
|
||||
|
||||
pub use core::pin;
|
||||
|
||||
//- /corelib.rs crate:core
|
||||
|
||||
pub mod pin {
|
||||
pub struct Pin;
|
||||
}
|
||||
"#,
|
||||
"std::pin::Pin",
|
||||
expect![[r#"
|
||||
Plain (imports ✔): core::pin::Pin
|
||||
Plain (imports ✖): core::pin::Pin
|
||||
ByCrate(imports ✔): core::pin::Pin
|
||||
ByCrate(imports ✖): core::pin::Pin
|
||||
BySelf (imports ✔): core::pin::Pin
|
||||
BySelf (imports ✖): core::pin::Pin
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_core_paths_over_std() {
|
||||
check_found_path_prefer_no_std(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
|
||||
$0
|
||||
|
||||
//- /std.rs crate:std deps:core
|
||||
|
||||
pub mod fmt {
|
||||
pub use core::fmt::Error;
|
||||
}
|
||||
|
||||
//- /zzz.rs crate:core
|
||||
|
||||
pub mod fmt {
|
||||
pub struct Error;
|
||||
}
|
||||
"#,
|
||||
"core::fmt::Error",
|
||||
expect![[r#"
|
||||
Plain (imports ✔): core::fmt::Error
|
||||
Plain (imports ✖): core::fmt::Error
|
||||
ByCrate(imports ✔): core::fmt::Error
|
||||
ByCrate(imports ✖): core::fmt::Error
|
||||
BySelf (imports ✔): core::fmt::Error
|
||||
BySelf (imports ✖): core::fmt::Error
|
||||
"#]],
|
||||
);
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
|
|
@ -1878,10 +1932,9 @@ pub mod ops {
|
|||
|
||||
#[test]
|
||||
fn respect_unstable_modules() {
|
||||
check_found_path(
|
||||
check_found_path_prefer_no_std(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std,core
|
||||
#![no_std]
|
||||
extern crate std;
|
||||
$0
|
||||
//- /longer.rs crate:std deps:core
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ use crate::{
|
|||
|
||||
type FxIndexMap<K, V> =
|
||||
indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||
/// A wrapper around two booleans, [`ImportPathConfig::prefer_no_std`] and [`ImportPathConfig::prefer_prelude`].
|
||||
/// A wrapper around three booleans
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
pub struct ImportPathConfig {
|
||||
/// If true, prefer to unconditionally use imports of the `core` and `alloc` crate
|
||||
|
|
|
|||
|
|
@ -1201,7 +1201,6 @@ macro_rules! m {
|
|||
|
||||
#[test]
|
||||
fn test_meta_doc_comments() {
|
||||
cov_mark::check!(test_meta_doc_comments);
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
|
|
|
|||
|
|
@ -317,9 +317,9 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
|
|||
_: Span,
|
||||
_: Span,
|
||||
) -> Result<Subtree, ProcMacroExpansionError> {
|
||||
let (parse, _) = ::mbe::token_tree_to_syntax_node(
|
||||
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
|
||||
subtree,
|
||||
::mbe::TopEntryPoint::MacroItems,
|
||||
syntax_bridge::TopEntryPoint::MacroItems,
|
||||
span::Edition::CURRENT,
|
||||
);
|
||||
if parse.errors().is_empty() {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ use crate::{
|
|||
};
|
||||
|
||||
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
|
||||
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
|
||||
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
|
||||
|
||||
pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
|
||||
|
|
@ -1440,7 +1439,14 @@ impl DefCollector<'_> {
|
|||
depth: usize,
|
||||
container: ItemContainerId,
|
||||
) {
|
||||
if EXPANSION_DEPTH_LIMIT.check(depth).is_err() {
|
||||
let recursion_limit = self.def_map.recursion_limit() as usize;
|
||||
let recursion_limit = Limit::new(if cfg!(test) {
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
std::cmp::min(32, recursion_limit)
|
||||
} else {
|
||||
recursion_limit
|
||||
});
|
||||
if recursion_limit.check(depth).is_err() {
|
||||
cov_mark::hit!(macro_expansion_overflow);
|
||||
tracing::warn!("macro expansion is too deep");
|
||||
return;
|
||||
|
|
@ -2003,7 +2009,7 @@ impl ModCollector<'_, '_> {
|
|||
Err(cfg) => {
|
||||
self.emit_unconfigured_diagnostic(
|
||||
self.tree_id,
|
||||
AttrOwner::TopLevel,
|
||||
AttrOwner::ModItem(module_id.into()),
|
||||
&cfg,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use base_db::{SourceDatabase, SourceDatabaseExt2 as _};
|
||||
use base_db::{SourceDatabase, SourceDatabaseFileInputExt as _};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
|
||||
|
|
|
|||
|
|
@ -194,6 +194,11 @@ pub(super) fn lower_generic_args(
|
|||
match generic_arg {
|
||||
ast::GenericArg::TypeArg(type_arg) => {
|
||||
let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
|
||||
type_ref.walk(&mut |tr| {
|
||||
if let TypeRef::ImplTrait(bounds) = tr {
|
||||
lower_ctx.update_impl_traits_bounds(bounds.clone());
|
||||
}
|
||||
});
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[salsa::database(
|
||||
base_db::SourceDatabaseExtStorage,
|
||||
base_db::SourceRootDatabaseStorage,
|
||||
base_db::SourceDatabaseStorage,
|
||||
hir_expand::db::ExpandDatabaseStorage,
|
||||
crate::db::InternDatabaseStorage,
|
||||
|
|
@ -69,9 +69,6 @@ impl fmt::Debug for TestDB {
|
|||
impl panic::RefUnwindSafe for TestDB {}
|
||||
|
||||
impl FileLoader for TestDB {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str> {
|
||||
FileLoaderDelegate(self).file_text(file_id)
|
||||
}
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
FileLoaderDelegate(self).resolve_path(path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "hir-expand"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
repository.workspace = true
|
||||
description = "Macro expansion for rust-analyzer."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
@ -33,6 +34,7 @@ mbe.workspace = true
|
|||
limit.workspace = true
|
||||
span.workspace = true
|
||||
parser.workspace = true
|
||||
syntax-bridge.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@ use cfg::CfgExpr;
|
|||
use either::Either;
|
||||
use intern::{sym, Interned, Symbol};
|
||||
|
||||
use mbe::{
|
||||
desugar_doc_comment_text, syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode,
|
||||
Punct,
|
||||
};
|
||||
use mbe::{DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{Span, SyntaxContextId};
|
||||
use syntax::unescape;
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode};
|
||||
use syntax_bridge::{desugar_doc_comment_text, syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use triomphe::ThinArc;
|
||||
|
||||
use crate::name::Name;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
use intern::sym;
|
||||
use itertools::izip;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{MacroCallId, Span};
|
||||
use stdx::never;
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -209,9 +209,9 @@ struct BasicAdtInfo {
|
|||
}
|
||||
|
||||
fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (parsed, tm) = &mbe::token_tree_to_syntax_node(
|
||||
let (parsed, tm) = &syntax_bridge::token_tree_to_syntax_node(
|
||||
tt,
|
||||
mbe::TopEntryPoint::MacroItems,
|
||||
syntax_bridge::TopEntryPoint::MacroItems,
|
||||
parser::Edition::CURRENT_FIXME,
|
||||
);
|
||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node())
|
||||
|
|
@ -268,7 +268,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
|||
match this {
|
||||
Some(it) => {
|
||||
param_type_set.insert(it.as_name());
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
it.syntax(),
|
||||
tm,
|
||||
call_site,
|
||||
|
|
@ -282,7 +282,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
|||
};
|
||||
let bounds = match ¶m {
|
||||
ast::TypeOrConstParam::Type(it) => it.type_bound_list().map(|it| {
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
it.syntax(),
|
||||
tm,
|
||||
call_site,
|
||||
|
|
@ -295,7 +295,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
|||
let ty = param
|
||||
.ty()
|
||||
.map(|ty| {
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
ty.syntax(),
|
||||
tm,
|
||||
call_site,
|
||||
|
|
@ -316,7 +316,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
|||
let where_clause = if let Some(w) = where_clause {
|
||||
w.predicates()
|
||||
.map(|it| {
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
it.syntax(),
|
||||
tm,
|
||||
call_site,
|
||||
|
|
@ -353,7 +353,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
|||
param_type_set.contains(&name).then_some(p)
|
||||
})
|
||||
.map(|it| {
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
it.syntax(),
|
||||
tm,
|
||||
call_site,
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ use base_db::AnchoredPath;
|
|||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use intern::{sym, Symbol};
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree, DelimiterKind};
|
||||
use mbe::{expect_fragment, DelimiterKind};
|
||||
use span::{Edition, EditionedFileId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
format_smolstr,
|
||||
unescape::{unescape_byte, unescape_char, unescape_unicode, Mode},
|
||||
};
|
||||
use syntax_bridge::parse_to_token_tree;
|
||||
|
||||
use crate::{
|
||||
builtin::quote::{dollar_crate, quote},
|
||||
|
|
@ -228,20 +229,22 @@ fn assert_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id);
|
||||
let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT_FIXME);
|
||||
|
||||
let mut iter = ::tt::iter::TtIter::new(tt);
|
||||
|
||||
let cond = expect_fragment(
|
||||
&mut iter,
|
||||
parser::PrefixEntryPoint::Expr,
|
||||
db.crate_graph()[id.lookup(db).krate].edition,
|
||||
tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close },
|
||||
);
|
||||
_ = iter.expect_char(',');
|
||||
let rest = iter.as_slice();
|
||||
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let expanded = match &*args {
|
||||
[cond, panic_args @ ..] => {
|
||||
let comma = tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(call_site_span),
|
||||
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
span: call_site_span,
|
||||
}))]),
|
||||
};
|
||||
let cond = cond.clone();
|
||||
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
|
||||
let expanded = match cond.value {
|
||||
Some(cond) => {
|
||||
let panic_args = rest.iter().cloned();
|
||||
let mac = if use_panic_2021(db, span) {
|
||||
quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) }
|
||||
} else {
|
||||
|
|
@ -253,10 +256,13 @@ fn assert_expand(
|
|||
}
|
||||
}}
|
||||
}
|
||||
[] => quote! {call_site_span =>{}},
|
||||
None => quote! {call_site_span =>{}},
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
match cond.err {
|
||||
Some(err) => ExpandResult::new(expanded, err.into()),
|
||||
None => ExpandResult::ok(expanded),
|
||||
}
|
||||
}
|
||||
|
||||
fn file_expand(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Defines a unit of change that can applied to the database to get the next
|
||||
//! state. Changes are transactional.
|
||||
use base_db::{
|
||||
salsa::Durability, CrateGraph, CrateId, FileChange, SourceDatabaseExt, SourceRoot,
|
||||
salsa::Durability, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootDatabase,
|
||||
TargetLayoutLoadResult, Version,
|
||||
};
|
||||
use la_arena::RawIdx;
|
||||
|
|
@ -23,7 +23,7 @@ impl ChangeWithProcMacros {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceDatabaseExt)) {
|
||||
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceRootDatabase)) {
|
||||
self.source_change.apply(db);
|
||||
if let Some(proc_macros) = self.proc_macros {
|
||||
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
use base_db::{salsa, CrateId, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex};
|
||||
use mbe::MatchedArmIndex;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
|
||||
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
|
||||
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -165,7 +166,7 @@ pub fn expand_speculative(
|
|||
// Build the subtree and token mapping for the speculative args
|
||||
let (mut tt, undo_info) = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => (
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
speculative_args,
|
||||
span_map,
|
||||
span,
|
||||
|
|
@ -178,7 +179,7 @@ pub fn expand_speculative(
|
|||
SyntaxFixupUndoInfo::NONE,
|
||||
),
|
||||
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
speculative_args,
|
||||
span_map,
|
||||
span,
|
||||
|
|
@ -213,7 +214,7 @@ pub fn expand_speculative(
|
|||
fixups.remove.extend(censor_cfg);
|
||||
|
||||
(
|
||||
mbe::syntax_node_to_token_tree_modified(
|
||||
syntax_bridge::syntax_node_to_token_tree_modified(
|
||||
speculative_args,
|
||||
span_map,
|
||||
fixups.append,
|
||||
|
|
@ -459,7 +460,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
|||
return dummy_tt(kind);
|
||||
}
|
||||
|
||||
let mut tt = mbe::syntax_node_to_token_tree(
|
||||
let mut tt = syntax_bridge::syntax_node_to_token_tree(
|
||||
tt.syntax(),
|
||||
map.as_ref(),
|
||||
span,
|
||||
|
|
@ -515,7 +516,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
|||
fixups.remove.extend(censor_cfg);
|
||||
|
||||
(
|
||||
mbe::syntax_node_to_token_tree_modified(
|
||||
syntax_bridge::syntax_node_to_token_tree_modified(
|
||||
syntax,
|
||||
map,
|
||||
fixups.append,
|
||||
|
|
@ -720,13 +721,13 @@ fn token_tree_to_syntax_node(
|
|||
edition: parser::Edition,
|
||||
) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
|
||||
let entry_point = match expand_to {
|
||||
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
|
||||
ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
|
||||
ExpandTo::Pattern => mbe::TopEntryPoint::Pattern,
|
||||
ExpandTo::Type => mbe::TopEntryPoint::Type,
|
||||
ExpandTo::Expr => mbe::TopEntryPoint::Expr,
|
||||
ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts,
|
||||
ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems,
|
||||
ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern,
|
||||
ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
|
||||
ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
|
||||
};
|
||||
mbe::token_tree_to_syntax_node(tt, entry_point, edition)
|
||||
syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition)
|
||||
}
|
||||
|
||||
fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
use base_db::CrateId;
|
||||
use intern::sym;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use span::{Edition, MacroCallId, Span, SyntaxContextId};
|
||||
use stdx::TupleExt;
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -112,7 +112,7 @@ impl DeclarativeMacroExpander {
|
|||
ast::Macro::MacroRules(macro_rules) => (
|
||||
match macro_rules.token_tree() {
|
||||
Some(arg) => {
|
||||
let tt = mbe::syntax_node_to_token_tree(
|
||||
let tt = syntax_bridge::syntax_node_to_token_tree(
|
||||
arg.syntax(),
|
||||
map.as_ref(),
|
||||
map.span_for_range(
|
||||
|
|
@ -135,14 +135,14 @@ impl DeclarativeMacroExpander {
|
|||
let span =
|
||||
map.span_for_range(macro_def.macro_token().unwrap().text_range());
|
||||
let args = macro_def.args().map(|args| {
|
||||
mbe::syntax_node_to_token_tree(
|
||||
syntax_bridge::syntax_node_to_token_tree(
|
||||
args.syntax(),
|
||||
map.as_ref(),
|
||||
span,
|
||||
DocCommentDesugarMode::Mbe,
|
||||
)
|
||||
});
|
||||
let body = mbe::syntax_node_to_token_tree(
|
||||
let body = syntax_bridge::syntax_node_to_token_tree(
|
||||
body.syntax(),
|
||||
map.as_ref(),
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use base_db::CrateId;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -82,7 +82,7 @@ pub fn expand_eager_macro_input(
|
|||
return ExpandResult { value: None, err };
|
||||
};
|
||||
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(
|
||||
let mut subtree = syntax_bridge::syntax_node_to_token_tree(
|
||||
&expanded_eager_input,
|
||||
arg_map,
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
//! fix up syntax errors in the code we're passing to them.
|
||||
|
||||
use intern::sym;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
use span::{
|
||||
|
|
@ -14,6 +13,7 @@ use syntax::{
|
|||
ast::{self, AstNode, HasLoopBody},
|
||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
use tt::Spacing;
|
||||
|
||||
|
|
@ -76,7 +76,8 @@ pub(crate) fn fixup_syntax(
|
|||
if can_handle_error(&node) && has_error_to_handle(&node) {
|
||||
remove.insert(node.clone().into());
|
||||
// the node contains an error node, we have to completely replace it by something valid
|
||||
let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site, mode);
|
||||
let original_tree =
|
||||
syntax_bridge::syntax_node_to_token_tree(&node, span_map, call_site, mode);
|
||||
let idx = original.len() as u32;
|
||||
original.push(original_tree);
|
||||
let span = span_map.span_for_range(node_range);
|
||||
|
|
@ -434,9 +435,9 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use span::{Edition, EditionedFileId, FileId};
|
||||
use syntax::TextRange;
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -483,7 +484,7 @@ mod tests {
|
|||
span_map.span_for_range(TextRange::empty(0.into())),
|
||||
DocCommentDesugarMode::Mbe,
|
||||
);
|
||||
let mut tt = mbe::syntax_node_to_token_tree_modified(
|
||||
let mut tt = syntax_bridge::syntax_node_to_token_tree_modified(
|
||||
&parsed.syntax_node(),
|
||||
span_map.as_ref(),
|
||||
fixups.append,
|
||||
|
|
@ -498,9 +499,9 @@ mod tests {
|
|||
expect.assert_eq(&actual);
|
||||
|
||||
// the fixed-up tree should be syntactically valid
|
||||
let (parse, _) = mbe::token_tree_to_syntax_node(
|
||||
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
|
||||
&tt,
|
||||
::mbe::TopEntryPoint::MacroItems,
|
||||
syntax_bridge::TopEntryPoint::MacroItems,
|
||||
parser::Edition::CURRENT,
|
||||
);
|
||||
assert!(
|
||||
|
|
@ -513,7 +514,7 @@ mod tests {
|
|||
|
||||
// the fixed-up + reversed version should be equivalent to the original input
|
||||
// modulo token IDs and `Punct`s' spacing.
|
||||
let original_as_tt = mbe::syntax_node_to_token_tree(
|
||||
let original_as_tt = syntax_bridge::syntax_node_to_token_tree(
|
||||
&parsed.syntax_node(),
|
||||
span_map.as_ref(),
|
||||
span_map.span_for_range(TextRange::empty(0.into())),
|
||||
|
|
|
|||
|
|
@ -176,7 +176,12 @@ impl ExpandErrorKind {
|
|||
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
|
||||
match db.proc_macros().get_error_for_crate(def_crate) {
|
||||
Some((e, hard_err)) => (e.to_owned(), hard_err),
|
||||
None => ("missing expander".to_owned(), true),
|
||||
None => (
|
||||
format!(
|
||||
"internal error: proc-macro map is missing error entry for crate {def_crate:?}"
|
||||
),
|
||||
true,
|
||||
),
|
||||
}
|
||||
}
|
||||
ExpandErrorKind::MacroDefinition => {
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ pub enum SpanMapRef<'a> {
|
|||
RealSpanMap(&'a RealSpanMap),
|
||||
}
|
||||
|
||||
impl mbe::SpanMapper<Span> for SpanMap {
|
||||
impl syntax_bridge::SpanMapper<Span> for SpanMap {
|
||||
fn span_for(&self, range: TextRange) -> Span {
|
||||
self.span_for_range(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl mbe::SpanMapper<Span> for SpanMapRef<'_> {
|
||||
impl syntax_bridge::SpanMapper<Span> for SpanMapRef<'_> {
|
||||
fn span_for(&self, range: TextRange) -> Span {
|
||||
self.span_for_range(range)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "hir-ty"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
repository.workspace = true
|
||||
description = "The type system for rust-analyzer."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
|||
};
|
||||
chalk_ir::Binders::new(binders, bound)
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
|
||||
let datas = self
|
||||
.db
|
||||
.type_alias_impl_traits(alias)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue