Merge from rustc
This commit is contained in:
commit
f590fa9214
715 changed files with 16769 additions and 7776 deletions
|
|
@ -48,9 +48,9 @@ fn main() {
|
|||
err => {
|
||||
drop(err);
|
||||
if let Ok(pid) = pid {
|
||||
println!("WARNING: build directory locked by process {pid}, waiting for lock");
|
||||
eprintln!("WARNING: build directory locked by process {pid}, waiting for lock");
|
||||
} else {
|
||||
println!("WARNING: build directory locked, waiting for lock");
|
||||
eprintln!("WARNING: build directory locked, waiting for lock");
|
||||
}
|
||||
let mut lock = t!(build_lock.write());
|
||||
t!(lock.write(process::id().to_string().as_ref()));
|
||||
|
|
@ -70,13 +70,13 @@ fn main() {
|
|||
// changelog warning, not the `x.py setup` message.
|
||||
let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. });
|
||||
if suggest_setup {
|
||||
println!("WARNING: you have not made a `config.toml`");
|
||||
println!(
|
||||
eprintln!("WARNING: you have not made a `config.toml`");
|
||||
eprintln!(
|
||||
"HELP: consider running `./x.py setup` or copying `config.example.toml` by running \
|
||||
`cp config.example.toml config.toml`"
|
||||
);
|
||||
} else if let Some(suggestion) = &changelog_suggestion {
|
||||
println!("{suggestion}");
|
||||
eprintln!("{suggestion}");
|
||||
}
|
||||
|
||||
let pre_commit = config.src.join(".git").join("hooks").join("pre-commit");
|
||||
|
|
@ -86,13 +86,13 @@ fn main() {
|
|||
Build::new(config).build();
|
||||
|
||||
if suggest_setup {
|
||||
println!("WARNING: you have not made a `config.toml`");
|
||||
println!(
|
||||
eprintln!("WARNING: you have not made a `config.toml`");
|
||||
eprintln!(
|
||||
"HELP: consider running `./x.py setup` or copying `config.example.toml` by running \
|
||||
`cp config.example.toml config.toml`"
|
||||
);
|
||||
} else if let Some(suggestion) = &changelog_suggestion {
|
||||
println!("{suggestion}");
|
||||
eprintln!("{suggestion}");
|
||||
}
|
||||
|
||||
// Give a warning if the pre-commit script is in pre-commit and not pre-push.
|
||||
|
|
@ -102,14 +102,14 @@ fn main() {
|
|||
if fs::read_to_string(pre_commit).is_ok_and(|contents| {
|
||||
contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570")
|
||||
}) {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: You have the pre-push script installed to .git/hooks/pre-commit. \
|
||||
Consider moving it to .git/hooks/pre-push instead, which runs less often."
|
||||
);
|
||||
}
|
||||
|
||||
if suggest_setup || changelog_suggestion.is_some() {
|
||||
println!("NOTE: this message was printed twice to make it more likely to be seen");
|
||||
eprintln!("NOTE: this message was printed twice to make it more likely to be seen");
|
||||
}
|
||||
|
||||
if dump_bootstrap_shims {
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ fn main() {
|
|||
// should run on success, after this block.
|
||||
}
|
||||
if verbose > 0 {
|
||||
println!("\nDid not run successfully: {status}\n{cmd:?}\n-------------");
|
||||
eprintln!("\nDid not run successfully: {status}\n{cmd:?}\n-------------");
|
||||
}
|
||||
|
||||
if let Some(mut on_fail) = on_fail {
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ impl Step for CodegenBackend {
|
|||
fn run(self, builder: &Builder<'_>) {
|
||||
// FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved
|
||||
if builder.build.config.vendor && self.backend == "gcc" {
|
||||
println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
|
||||
eprintln!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1611,7 +1611,7 @@ impl Step for Sysroot {
|
|||
let sysroot = sysroot_dir(compiler.stage);
|
||||
|
||||
builder
|
||||
.verbose(|| println!("Removing sysroot {} to avoid caching bugs", sysroot.display()));
|
||||
.verbose(|| eprintln!("Removing sysroot {} to avoid caching bugs", sysroot.display()));
|
||||
let _ = fs::remove_dir_all(&sysroot);
|
||||
t!(fs::create_dir_all(&sysroot));
|
||||
|
||||
|
|
@ -1681,7 +1681,7 @@ impl Step for Sysroot {
|
|||
return true;
|
||||
}
|
||||
if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) {
|
||||
builder.verbose_than(1, || println!("ignoring {}", path.display()));
|
||||
builder.verbose_than(1, || eprintln!("ignoring {}", path.display()));
|
||||
false
|
||||
} else {
|
||||
true
|
||||
|
|
@ -2240,7 +2240,7 @@ pub fn stream_cargo(
|
|||
cargo.arg(arg);
|
||||
}
|
||||
|
||||
builder.verbose(|| println!("running: {cargo:?}"));
|
||||
builder.verbose(|| eprintln!("running: {cargo:?}"));
|
||||
|
||||
if builder.config.dry_run() {
|
||||
return true;
|
||||
|
|
@ -2266,7 +2266,7 @@ pub fn stream_cargo(
|
|||
cb(msg)
|
||||
}
|
||||
// If this was informational, just print it out and continue
|
||||
Err(_) => println!("{line}"),
|
||||
Err(_) => eprintln!("{line}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2080,7 +2080,7 @@ fn maybe_install_llvm(
|
|||
{
|
||||
let mut cmd = command(llvm_config);
|
||||
cmd.arg("--libfiles");
|
||||
builder.verbose(|| println!("running {cmd:?}"));
|
||||
builder.verbose(|| eprintln!("running {cmd:?}"));
|
||||
let files = cmd.run_capture_stdout(builder).stdout();
|
||||
let build_llvm_out = &builder.llvm_out(builder.config.build);
|
||||
let target_llvm_out = &builder.llvm_out(target);
|
||||
|
|
|
|||
|
|
@ -107,10 +107,10 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) {
|
|||
if let Some(adjective) = adjective { format!("{adjective} ") } else { String::new() };
|
||||
if len <= 10 {
|
||||
for path in paths {
|
||||
println!("fmt: {verb} {adjective}file {path}");
|
||||
eprintln!("fmt: {verb} {adjective}file {path}");
|
||||
}
|
||||
} else {
|
||||
println!("fmt: {verb} {len} {adjective}files");
|
||||
eprintln!("fmt: {verb} {len} {adjective}files");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
|
|||
match get_modified_rs_files(build) {
|
||||
Ok(Some(files)) => {
|
||||
if files.is_empty() {
|
||||
println!("fmt info: No modified files detected for formatting.");
|
||||
eprintln!("fmt info: No modified files detected for formatting.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ const SHELL: &str = "sh";
|
|||
|
||||
/// We have to run a few shell scripts, which choke quite a bit on both `\`
|
||||
/// characters and on `C:\` paths, so normalize both of them away.
|
||||
fn sanitize_sh(path: &Path) -> String {
|
||||
fn sanitize_sh(path: &Path, is_cygwin: bool) -> String {
|
||||
let path = path.to_str().unwrap().replace('\\', "/");
|
||||
return change_drive(unc_to_lfs(&path)).unwrap_or(path);
|
||||
return if is_cygwin { path } else { change_drive(unc_to_lfs(&path)).unwrap_or(path) };
|
||||
|
||||
fn unc_to_lfs(s: &str) -> &str {
|
||||
s.strip_prefix("//?/").unwrap_or(s)
|
||||
|
|
@ -71,6 +71,7 @@ fn install_sh(
|
|||
let prefix = default_path(&builder.config.prefix, "/usr/local");
|
||||
let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
|
||||
let destdir_env = env::var_os("DESTDIR").map(PathBuf::from);
|
||||
let is_cygwin = builder.config.build.is_cygwin();
|
||||
|
||||
// Sanity checks on the write access of user.
|
||||
//
|
||||
|
|
@ -103,14 +104,14 @@ fn install_sh(
|
|||
|
||||
let mut cmd = command(SHELL);
|
||||
cmd.current_dir(&empty_dir)
|
||||
.arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
|
||||
.arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix)))
|
||||
.arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir)))
|
||||
.arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir)))
|
||||
.arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir)))
|
||||
.arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir)))
|
||||
.arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir)))
|
||||
.arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir)))
|
||||
.arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"), is_cygwin))
|
||||
.arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix, is_cygwin)))
|
||||
.arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir, is_cygwin)))
|
||||
.arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir, is_cygwin)))
|
||||
.arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir, is_cygwin)))
|
||||
.arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir, is_cygwin)))
|
||||
.arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir, is_cygwin)))
|
||||
.arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir, is_cygwin)))
|
||||
.arg("--disable-ldconfig");
|
||||
cmd.run(builder);
|
||||
t!(fs::remove_dir_all(&empty_dir));
|
||||
|
|
@ -120,7 +121,7 @@ fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
|
|||
config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))
|
||||
}
|
||||
|
||||
fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf) -> String {
|
||||
fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf, is_cygwin: bool) -> String {
|
||||
// The DESTDIR environment variable is a standard way to install software in a subdirectory
|
||||
// while keeping the original directory structure, even if the prefix or other directories
|
||||
// contain absolute paths.
|
||||
|
|
@ -146,7 +147,7 @@ fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf) -> String {
|
|||
assert!(path.is_absolute(), "could not make the path relative");
|
||||
}
|
||||
|
||||
sanitize_sh(&path)
|
||||
sanitize_sh(&path, is_cygwin)
|
||||
}
|
||||
|
||||
macro_rules! install {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ impl Step for Profile {
|
|||
t!(fs::remove_file(path));
|
||||
}
|
||||
_ => {
|
||||
println!("Exiting.");
|
||||
eprintln!("Exiting.");
|
||||
crate::exit!(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -184,15 +184,15 @@ pub fn setup(config: &Config, profile: Profile) {
|
|||
Profile::Dist => &["dist", "build"],
|
||||
};
|
||||
|
||||
println!();
|
||||
eprintln!();
|
||||
|
||||
println!("To get started, try one of the following commands:");
|
||||
eprintln!("To get started, try one of the following commands:");
|
||||
for cmd in suggestions {
|
||||
println!("- `x.py {cmd}`");
|
||||
eprintln!("- `x.py {cmd}`");
|
||||
}
|
||||
|
||||
if profile != Profile::Dist {
|
||||
println!(
|
||||
eprintln!(
|
||||
"For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
|
||||
);
|
||||
}
|
||||
|
|
@ -224,7 +224,7 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) {
|
|||
t!(fs::write(path, settings));
|
||||
|
||||
let include_path = profile.include_path(&config.src);
|
||||
println!("`x.py` will now use the configuration at {}", include_path.display());
|
||||
eprintln!("`x.py` will now use the configuration at {}", include_path.display());
|
||||
}
|
||||
|
||||
/// Creates a toolchain link for stage1 using `rustup`
|
||||
|
|
@ -256,7 +256,7 @@ impl Step for Link {
|
|||
}
|
||||
|
||||
if !rustup_installed(builder) {
|
||||
println!("WARNING: `rustup` is not installed; Skipping `stage1` toolchain linking.");
|
||||
eprintln!("WARNING: `rustup` is not installed; Skipping `stage1` toolchain linking.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ fn attempt_toolchain_link(builder: &Builder<'_>, stage_path: &str) {
|
|||
}
|
||||
|
||||
if try_link_toolchain(builder, stage_path) {
|
||||
println!(
|
||||
eprintln!(
|
||||
"Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain"
|
||||
);
|
||||
} else {
|
||||
|
|
@ -321,14 +321,14 @@ fn toolchain_is_linked(builder: &Builder<'_>) -> bool {
|
|||
return false;
|
||||
}
|
||||
// The toolchain has already been linked.
|
||||
println!(
|
||||
eprintln!(
|
||||
"`stage1` toolchain already linked; not attempting to link `stage1` toolchain"
|
||||
);
|
||||
}
|
||||
None => {
|
||||
// In this case, we don't know if the `stage1` toolchain has been linked;
|
||||
// but `rustup` failed, so let's not go any further.
|
||||
println!(
|
||||
eprintln!(
|
||||
"`rustup` failed to list current toolchains; not attempting to link `stage1` toolchain"
|
||||
);
|
||||
}
|
||||
|
|
@ -389,12 +389,12 @@ pub fn interactive_path() -> io::Result<Profile> {
|
|||
input.parse()
|
||||
}
|
||||
|
||||
println!("Welcome to the Rust project! What do you want to do with x.py?");
|
||||
eprintln!("Welcome to the Rust project! What do you want to do with x.py?");
|
||||
for ((letter, _), profile) in abbrev_all() {
|
||||
println!("{}) {}: {}", letter, profile, profile.purpose());
|
||||
eprintln!("{}) {}: {}", letter, profile, profile.purpose());
|
||||
}
|
||||
let template = loop {
|
||||
print!(
|
||||
eprint!(
|
||||
"Please choose one ({}): ",
|
||||
abbrev_all().map(|((l, _), _)| l).collect::<Vec<_>>().join("/")
|
||||
);
|
||||
|
|
@ -428,7 +428,7 @@ enum PromptResult {
|
|||
fn prompt_user(prompt: &str) -> io::Result<Option<PromptResult>> {
|
||||
let mut input = String::new();
|
||||
loop {
|
||||
print!("{prompt} ");
|
||||
eprint!("{prompt} ");
|
||||
io::stdout().flush()?;
|
||||
input.clear();
|
||||
io::stdin().read_line(&mut input)?;
|
||||
|
|
@ -490,7 +490,7 @@ fn install_git_hook_maybe(builder: &Builder<'_>, config: &Config) -> io::Result<
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
println!(
|
||||
eprintln!(
|
||||
"\nRust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
|
||||
If you'd like, x.py can install a git hook for you that will automatically run `test tidy` before
|
||||
pushing your code to ensure your code is up to par. If you decide later that this behavior is
|
||||
|
|
@ -498,7 +498,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
|
|||
);
|
||||
|
||||
if prompt_user("Would you like to install the git hook?: [y/N]")? != Some(PromptResult::Yes) {
|
||||
println!("Ok, skipping installation!");
|
||||
eprintln!("Ok, skipping installation!");
|
||||
return Ok(());
|
||||
}
|
||||
if !hooks_dir.exists() {
|
||||
|
|
@ -515,7 +515,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
|
|||
);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(_) => println!("Linked `src/etc/pre-push.sh` to `.git/hooks/pre-push`"),
|
||||
Ok(_) => eprintln!("Linked `src/etc/pre-push.sh` to `.git/hooks/pre-push`"),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -541,7 +541,7 @@ Select which editor you would like to set up [default: None]: ";
|
|||
|
||||
let mut input = String::new();
|
||||
loop {
|
||||
print!("{}", prompt_str);
|
||||
eprint!("{}", prompt_str);
|
||||
io::stdout().flush()?;
|
||||
input.clear();
|
||||
io::stdin().read_line(&mut input)?;
|
||||
|
|
@ -656,7 +656,7 @@ impl Step for Editor {
|
|||
if let Some(editor_kind) = editor_kind {
|
||||
while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {}
|
||||
} else {
|
||||
println!("Ok, skipping editor setup!");
|
||||
eprintln!("Ok, skipping editor setup!");
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("Could not determine the editor: {e}"),
|
||||
|
|
@ -689,7 +689,7 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu
|
|||
mismatched_settings = Some(false);
|
||||
}
|
||||
}
|
||||
println!(
|
||||
eprintln!(
|
||||
"\nx.py can automatically install the recommended `{settings_filename}` file for rustc development"
|
||||
);
|
||||
|
||||
|
|
@ -708,7 +708,7 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu
|
|||
Some(PromptResult::Yes) => true,
|
||||
Some(PromptResult::Print) => false,
|
||||
_ => {
|
||||
println!("Ok, skipping settings!");
|
||||
eprintln!("Ok, skipping settings!");
|
||||
return Ok(true);
|
||||
}
|
||||
};
|
||||
|
|
@ -735,9 +735,9 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu
|
|||
_ => "Created",
|
||||
};
|
||||
fs::write(&settings_path, editor.settings_template())?;
|
||||
println!("{verb} `{}`", settings_filename);
|
||||
eprintln!("{verb} `{}`", settings_filename);
|
||||
} else {
|
||||
println!("\n{}", editor.settings_template());
|
||||
eprintln!("\n{}", editor.settings_template());
|
||||
}
|
||||
Ok(should_create)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,6 @@ pub fn suggest(builder: &Builder<'_>, run: bool) {
|
|||
build.build();
|
||||
}
|
||||
} else {
|
||||
println!("HELP: consider using the `--run` flag to automatically run suggested tests");
|
||||
eprintln!("HELP: consider using the `--run` flag to automatically run suggested tests");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,11 +471,11 @@ impl Miri {
|
|||
// We re-use the `cargo` from above.
|
||||
cargo.arg("--print-sysroot");
|
||||
|
||||
builder.verbose(|| println!("running: {cargo:?}"));
|
||||
builder.verbose(|| eprintln!("running: {cargo:?}"));
|
||||
let stdout = cargo.run_capture_stdout(builder).stdout();
|
||||
// Output is "<sysroot>\n".
|
||||
let sysroot = stdout.trim_end();
|
||||
builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
|
||||
builder.verbose(|| eprintln!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
|
||||
PathBuf::from(sysroot)
|
||||
}
|
||||
}
|
||||
|
|
@ -2488,7 +2488,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
|
|||
}
|
||||
}
|
||||
|
||||
builder.verbose(|| println!("doc tests for: {}", markdown.display()));
|
||||
builder.verbose(|| eprintln!("doc tests for: {}", markdown.display()));
|
||||
let mut cmd = builder.rustdoc_cmd(compiler);
|
||||
builder.add_rust_test_threads(&mut cmd);
|
||||
// allow for unstable options such as new editions
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ impl Builder<'_> {
|
|||
|
||||
let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
|
||||
if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) {
|
||||
println!("using sysroot {sysroot_str}");
|
||||
eprintln!("using sysroot {sysroot_str}");
|
||||
}
|
||||
|
||||
let mut rustflags = Rustflags::new(target);
|
||||
|
|
|
|||
|
|
@ -392,14 +392,14 @@ impl StepDescription {
|
|||
fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
|
||||
if builder.config.skip.iter().any(|e| pathset.has(e, builder.kind)) {
|
||||
if !matches!(builder.config.dry_run, DryRun::SelfCheck) {
|
||||
println!("Skipping {pathset:?} because it is excluded");
|
||||
eprintln!("Skipping {pathset:?} because it is excluded");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if !builder.config.skip.is_empty() && !matches!(builder.config.dry_run, DryRun::SelfCheck) {
|
||||
builder.verbose(|| {
|
||||
println!(
|
||||
eprintln!(
|
||||
"{:?} not skipped for {:?} -- not in {:?}",
|
||||
pathset, self.name, builder.config.skip
|
||||
)
|
||||
|
|
@ -1437,11 +1437,11 @@ impl<'a> Builder<'a> {
|
|||
panic!("{}", out);
|
||||
}
|
||||
if let Some(out) = self.cache.get(&step) {
|
||||
self.verbose_than(1, || println!("{}c {:?}", " ".repeat(stack.len()), step));
|
||||
self.verbose_than(1, || eprintln!("{}c {:?}", " ".repeat(stack.len()), step));
|
||||
|
||||
return out;
|
||||
}
|
||||
self.verbose_than(1, || println!("{}> {:?}", " ".repeat(stack.len()), step));
|
||||
self.verbose_than(1, || eprintln!("{}> {:?}", " ".repeat(stack.len()), step));
|
||||
stack.push(Box::new(step.clone()));
|
||||
}
|
||||
|
||||
|
|
@ -1462,7 +1462,7 @@ impl<'a> Builder<'a> {
|
|||
let step_string = format!("{step:?}");
|
||||
let brace_index = step_string.find('{').unwrap_or(0);
|
||||
let type_string = type_name::<S>();
|
||||
println!(
|
||||
eprintln!(
|
||||
"[TIMING] {} {} -- {}.{:03}",
|
||||
&type_string.strip_prefix("bootstrap::").unwrap_or(type_string),
|
||||
&step_string[brace_index..],
|
||||
|
|
@ -1479,7 +1479,9 @@ impl<'a> Builder<'a> {
|
|||
let cur_step = stack.pop().expect("step stack empty");
|
||||
assert_eq!(cur_step.downcast_ref(), Some(&step));
|
||||
}
|
||||
self.verbose_than(1, || println!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step));
|
||||
self.verbose_than(1, || {
|
||||
eprintln!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step)
|
||||
});
|
||||
self.cache.put(step, out.clone());
|
||||
out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -565,6 +565,12 @@ impl TargetSelection {
|
|||
self.ends_with("windows-gnu")
|
||||
}
|
||||
|
||||
pub fn is_cygwin(&self) -> bool {
|
||||
self.is_windows() &&
|
||||
// ref. https://cygwin.com/pipermail/cygwin/2022-February/250802.html
|
||||
env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
|
||||
}
|
||||
|
||||
/// Path to the file defining the custom target, if any.
|
||||
pub fn filepath(&self) -> Option<&Path> {
|
||||
self.file.as_ref().map(Path::new)
|
||||
|
|
@ -1293,7 +1299,7 @@ impl Config {
|
|||
.map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids))
|
||||
{
|
||||
if !changes.is_empty() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: There have been changes to x.py since you last updated:\n{}",
|
||||
crate::human_readable_changes(&changes)
|
||||
);
|
||||
|
|
@ -1559,7 +1565,7 @@ impl Config {
|
|||
}
|
||||
|
||||
if cargo_clippy.is_some() && rustc.is_none() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: Using `build.cargo-clippy` without `build.rustc` usually fails due to toolchain conflict."
|
||||
);
|
||||
}
|
||||
|
|
@ -1632,7 +1638,6 @@ impl Config {
|
|||
set(&mut config.docs_minification, docs_minification);
|
||||
set(&mut config.docs, docs);
|
||||
set(&mut config.locked_deps, locked_deps);
|
||||
set(&mut config.vendor, vendor);
|
||||
set(&mut config.full_bootstrap, full_bootstrap);
|
||||
set(&mut config.extended, extended);
|
||||
config.tools = tools;
|
||||
|
|
@ -1711,6 +1716,12 @@ impl Config {
|
|||
config.in_tree_llvm_info = GitInfo::new(false, &config.src.join("src/llvm-project"));
|
||||
config.in_tree_gcc_info = GitInfo::new(false, &config.src.join("src/gcc"));
|
||||
|
||||
config.vendor = vendor.unwrap_or(
|
||||
config.rust_info.is_from_tarball()
|
||||
&& config.src.join("vendor").exists()
|
||||
&& config.src.join(".cargo/config.toml").exists(),
|
||||
);
|
||||
|
||||
if let Some(rust) = toml.rust {
|
||||
let Rust {
|
||||
optimize: optimize_toml,
|
||||
|
|
@ -1841,7 +1852,7 @@ impl Config {
|
|||
|
||||
// FIXME: Remove this option at the end of 2024.
|
||||
if parallel_compiler.is_some() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: The `rust.parallel-compiler` option is deprecated and does nothing. The parallel compiler (with one thread) is now the default"
|
||||
);
|
||||
}
|
||||
|
|
@ -1873,7 +1884,7 @@ impl Config {
|
|||
if available_backends.contains(&backend) {
|
||||
panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'.");
|
||||
} else {
|
||||
println!("HELP: '{s}' for 'rust.codegen-backends' might fail. \
|
||||
eprintln!("HELP: '{s}' for 'rust.codegen-backends' might fail. \
|
||||
Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
|
||||
In this case, it would be referred to as '{backend}'.");
|
||||
}
|
||||
|
|
@ -1902,7 +1913,7 @@ impl Config {
|
|||
// tests may fail due to using a different channel than the one used by the compiler during tests.
|
||||
if let Some(commit) = &config.download_rustc_commit {
|
||||
if is_user_configured_rust_channel {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
|
||||
);
|
||||
|
||||
|
|
@ -1992,10 +2003,10 @@ impl Config {
|
|||
|
||||
if config.llvm_from_ci {
|
||||
let warn = |option: &str| {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: `{option}` will only be used on `compiler/rustc_llvm` build, not for the LLVM build."
|
||||
);
|
||||
println!(
|
||||
eprintln!(
|
||||
"HELP: To use `{option}` for LLVM builds, set `download-ci-llvm` option to false."
|
||||
);
|
||||
};
|
||||
|
|
@ -2014,12 +2025,12 @@ impl Config {
|
|||
// if they've chosen a different value.
|
||||
|
||||
if libzstd.is_some() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: when using `download-ci-llvm`, the local `llvm.libzstd` option, \
|
||||
like almost all `llvm.*` options, will be ignored and set by the LLVM CI \
|
||||
artifacts builder config."
|
||||
);
|
||||
println!(
|
||||
eprintln!(
|
||||
"HELP: To use `llvm.libzstd` for LLVM/LLD builds, set `download-ci-llvm` option to false."
|
||||
);
|
||||
}
|
||||
|
|
@ -2088,7 +2099,7 @@ impl Config {
|
|||
if available_backends.contains(&backend) {
|
||||
panic!("Invalid value '{s}' for 'target.{triple}.codegen-backends'. Instead, please use '{backend}'.");
|
||||
} else {
|
||||
println!("HELP: '{s}' for 'target.{triple}.codegen-backends' might fail. \
|
||||
eprintln!("HELP: '{s}' for 'target.{triple}.codegen-backends' might fail. \
|
||||
Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
|
||||
In this case, it would be referred to as '{backend}'.");
|
||||
}
|
||||
|
|
@ -2304,7 +2315,7 @@ impl Config {
|
|||
if self.dry_run() {
|
||||
return Ok(());
|
||||
}
|
||||
self.verbose(|| println!("running: {cmd:?}"));
|
||||
self.verbose(|| eprintln!("running: {cmd:?}"));
|
||||
build_helper::util::try_run(cmd, self.is_verbose())
|
||||
}
|
||||
|
||||
|
|
@ -2479,7 +2490,7 @@ impl Config {
|
|||
// This happens when LLVM submodule is updated in CI, we should disable ci-rustc without an error
|
||||
// to not break CI. For non-CI environments, we should return an error.
|
||||
if CiEnv::is_ci() {
|
||||
println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled.");
|
||||
eprintln!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled.");
|
||||
return None;
|
||||
} else {
|
||||
panic!("ERROR: LLVM submodule has changes, `download-rustc` can't be used.");
|
||||
|
|
@ -2490,8 +2501,8 @@ impl Config {
|
|||
let ci_config_toml = match self.get_builder_toml("ci-rustc") {
|
||||
Ok(ci_config_toml) => ci_config_toml,
|
||||
Err(e) if e.to_string().contains("unknown field") => {
|
||||
println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled.");
|
||||
println!("HELP: Consider rebasing to a newer commit if available.");
|
||||
eprintln!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled.");
|
||||
eprintln!("HELP: Consider rebasing to a newer commit if available.");
|
||||
return None;
|
||||
},
|
||||
Err(e) => {
|
||||
|
|
@ -2516,7 +2527,7 @@ impl Config {
|
|||
.is_some_and(|s| s == "1" || s == "true");
|
||||
|
||||
if disable_ci_rustc_if_incompatible && res.is_err() {
|
||||
println!("WARNING: download-rustc is disabled with `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` env.");
|
||||
eprintln!("WARNING: download-rustc is disabled with `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` env.");
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -2701,7 +2712,7 @@ impl Config {
|
|||
return;
|
||||
}
|
||||
|
||||
println!("Updating submodule {relative_path}");
|
||||
eprintln!("Updating submodule {relative_path}");
|
||||
self.check_run(
|
||||
helpers::git(Some(&self.src))
|
||||
.run_always()
|
||||
|
|
@ -2824,7 +2835,7 @@ impl Config {
|
|||
Some(StringOrBool::Bool(true)) => false,
|
||||
Some(StringOrBool::String(s)) if s == "if-unchanged" => {
|
||||
if !self.rust_info.is_managed_git_subrepository() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources."
|
||||
);
|
||||
crate::exit!(1);
|
||||
|
|
@ -2857,10 +2868,10 @@ impl Config {
|
|||
if if_unchanged {
|
||||
return None;
|
||||
}
|
||||
println!("ERROR: could not find commit hash for downloading rustc");
|
||||
println!("HELP: maybe your repository history is too shallow?");
|
||||
println!("HELP: consider setting `rust.download-rustc=false` in config.toml");
|
||||
println!("HELP: or fetch enough history to include one upstream commit");
|
||||
eprintln!("ERROR: could not find commit hash for downloading rustc");
|
||||
eprintln!("HELP: maybe your repository history is too shallow?");
|
||||
eprintln!("HELP: consider setting `rust.download-rustc=false` in config.toml");
|
||||
eprintln!("HELP: or fetch enough history to include one upstream commit");
|
||||
crate::exit!(1);
|
||||
}
|
||||
};
|
||||
|
|
@ -2899,7 +2910,7 @@ impl Config {
|
|||
let if_unchanged = || {
|
||||
if self.rust_info.is_from_tarball() {
|
||||
// Git is needed for running "if-unchanged" logic.
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`."
|
||||
);
|
||||
return false;
|
||||
|
|
@ -2948,10 +2959,10 @@ impl Config {
|
|||
// Only commits merged by bors will have CI artifacts.
|
||||
let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap();
|
||||
if commit.is_empty() {
|
||||
println!("error: could not find commit hash for downloading components from CI");
|
||||
println!("help: maybe your repository history is too shallow?");
|
||||
println!("help: consider disabling `{option_name}`");
|
||||
println!("help: or fetch enough history to include one upstream commit");
|
||||
eprintln!("error: could not find commit hash for downloading components from CI");
|
||||
eprintln!("help: maybe your repository history is too shallow?");
|
||||
eprintln!("help: consider disabling `{option_name}`");
|
||||
eprintln!("help: or fetch enough history to include one upstream commit");
|
||||
crate::exit!(1);
|
||||
}
|
||||
|
||||
|
|
@ -2963,14 +2974,14 @@ impl Config {
|
|||
if has_changes {
|
||||
if if_unchanged {
|
||||
if self.is_verbose() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"warning: saw changes to one of {modified_paths:?} since {commit}; \
|
||||
ignoring `{option_name}`"
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
println!(
|
||||
eprintln!(
|
||||
"warning: `{option_name}` is enabled, but there are changes to one of {modified_paths:?}"
|
||||
);
|
||||
}
|
||||
|
|
@ -3007,7 +3018,7 @@ pub(crate) fn check_incompatible_options_for_ci_llvm(
|
|||
($current:expr, $expected:expr) => {
|
||||
if let Some(current) = &$current {
|
||||
if Some(current) != $expected.as_ref() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: `llvm.{}` has no effect with `llvm.download-ci-llvm`. \
|
||||
Current value: {:?}, Expected value(s): {}{:?}",
|
||||
stringify!($expected).replace("_", "-"),
|
||||
|
|
@ -3112,7 +3123,7 @@ fn check_incompatible_options_for_ci_rustc(
|
|||
($current:expr, $expected:expr, $config_section:expr) => {
|
||||
if let Some(current) = &$current {
|
||||
if Some(current) != $expected.as_ref() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: `{}` has no effect with `rust.download-rustc`. \
|
||||
Current value: {:?}, Expected value(s): {}{:?}",
|
||||
format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
|
||||
|
|
|
|||
|
|
@ -196,12 +196,12 @@ impl Flags {
|
|||
if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
|
||||
HelpVerboseOnly::try_parse_from(normalize_args(args))
|
||||
{
|
||||
println!("NOTE: updating submodules before printing available paths");
|
||||
eprintln!("NOTE: updating submodules before printing available paths");
|
||||
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 {
|
||||
println!("{s}");
|
||||
eprintln!("{s}");
|
||||
} else {
|
||||
panic!("No paths available for subcommand `{}`", subcommand.as_str());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl Config {
|
|||
if self.dry_run() && !cmd.run_always {
|
||||
return true;
|
||||
}
|
||||
self.verbose(|| println!("running: {cmd:?}"));
|
||||
self.verbose(|| eprintln!("running: {cmd:?}"));
|
||||
check_run(cmd, self.is_verbose())
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ impl Config {
|
|||
/// Please see <https://nixos.org/patchelf.html> for more information
|
||||
fn fix_bin_or_dylib(&self, fname: &Path) {
|
||||
assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true));
|
||||
println!("attempting to patch {}", fname.display());
|
||||
eprintln!("attempting to patch {}", fname.display());
|
||||
|
||||
// Only build `.nix-deps` once.
|
||||
static NIX_DEPS_DIR: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
|
@ -206,7 +206,7 @@ impl Config {
|
|||
}
|
||||
|
||||
fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) {
|
||||
self.verbose(|| println!("download {url}"));
|
||||
self.verbose(|| eprintln!("download {url}"));
|
||||
// Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
|
||||
let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
|
||||
// While bootstrap itself only supports http and https downloads, downstream forks might
|
||||
|
|
@ -226,7 +226,7 @@ impl Config {
|
|||
}
|
||||
|
||||
fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
|
||||
println!("downloading {url}");
|
||||
eprintln!("downloading {url}");
|
||||
// Try curl. If that fails and we are on windows, fallback to PowerShell.
|
||||
// options should be kept in sync with
|
||||
// src/bootstrap/src/core/download.rs
|
||||
|
|
@ -341,7 +341,7 @@ impl Config {
|
|||
short_path = short_path.strip_prefix(pattern).unwrap_or(short_path);
|
||||
let dst_path = dst.join(short_path);
|
||||
self.verbose(|| {
|
||||
println!("extracting {} to {}", original_path.display(), dst.display())
|
||||
eprintln!("extracting {} to {}", original_path.display(), dst.display())
|
||||
});
|
||||
if !t!(member.unpack_in(dst)) {
|
||||
panic!("path traversal attack ??");
|
||||
|
|
@ -365,7 +365,7 @@ impl Config {
|
|||
pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool {
|
||||
use sha2::Digest;
|
||||
|
||||
self.verbose(|| println!("verifying {}", path.display()));
|
||||
self.verbose(|| eprintln!("verifying {}", path.display()));
|
||||
|
||||
if self.dry_run() {
|
||||
return false;
|
||||
|
|
@ -391,7 +391,7 @@ impl Config {
|
|||
let verified = checksum == expected;
|
||||
|
||||
if !verified {
|
||||
println!(
|
||||
eprintln!(
|
||||
"invalid checksum: \n\
|
||||
found: {checksum}\n\
|
||||
expected: {expected}",
|
||||
|
|
@ -421,7 +421,7 @@ enum DownloadSource {
|
|||
/// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions.
|
||||
impl Config {
|
||||
pub(crate) fn download_clippy(&self) -> PathBuf {
|
||||
self.verbose(|| println!("downloading stage0 clippy artifacts"));
|
||||
self.verbose(|| eprintln!("downloading stage0 clippy artifacts"));
|
||||
|
||||
let date = &self.stage0_metadata.compiler.date;
|
||||
let version = &self.stage0_metadata.compiler.version;
|
||||
|
|
@ -518,7 +518,7 @@ impl Config {
|
|||
}
|
||||
|
||||
pub(crate) fn download_ci_rustc(&self, commit: &str) {
|
||||
self.verbose(|| println!("using downloaded stage2 artifacts from CI (commit {commit})"));
|
||||
self.verbose(|| eprintln!("using downloaded stage2 artifacts from CI (commit {commit})"));
|
||||
|
||||
let version = self.artifact_version_part(commit);
|
||||
// download-rustc doesn't need its own cargo, it can just use beta's. But it does need the
|
||||
|
|
@ -539,7 +539,7 @@ impl Config {
|
|||
|
||||
#[cfg(not(feature = "bootstrap-self-test"))]
|
||||
pub(crate) fn download_beta_toolchain(&self) {
|
||||
self.verbose(|| println!("downloading stage0 beta artifacts"));
|
||||
self.verbose(|| eprintln!("downloading stage0 beta artifacts"));
|
||||
|
||||
let date = &self.stage0_metadata.compiler.date;
|
||||
let version = &self.stage0_metadata.compiler.version;
|
||||
|
|
@ -677,7 +677,7 @@ impl Config {
|
|||
return;
|
||||
} else {
|
||||
self.verbose(|| {
|
||||
println!(
|
||||
eprintln!(
|
||||
"ignoring cached file {} due to failed verification",
|
||||
tarball.display()
|
||||
)
|
||||
|
|
@ -776,10 +776,10 @@ download-rustc = false
|
|||
t!(check_incompatible_options_for_ci_llvm(current_config_toml, ci_config_toml));
|
||||
}
|
||||
Err(e) if e.to_string().contains("unknown field") => {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: CI LLVM has some fields that are no longer supported in bootstrap; download-ci-llvm will be disabled."
|
||||
);
|
||||
println!("HELP: Consider rebasing to a newer commit if available.");
|
||||
eprintln!("HELP: Consider rebasing to a newer commit if available.");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("ERROR: Failed to parse CI LLVM config.toml: {e}");
|
||||
|
|
|
|||
|
|
@ -237,11 +237,11 @@ than building it.
|
|||
stage0_supported_target_list.intersection(&missing_targets_hashset).collect();
|
||||
|
||||
if !duplicated_targets.is_empty() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list."
|
||||
);
|
||||
for duplicated_target in duplicated_targets {
|
||||
println!(" {duplicated_target}");
|
||||
eprintln!(" {duplicated_target}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,11 +406,11 @@ impl Build {
|
|||
.unwrap()
|
||||
.trim();
|
||||
if local_release.split('.').take(2).eq(version.split('.').take(2)) {
|
||||
build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
|
||||
build.verbose(|| eprintln!("auto-detected local-rebuild {local_release}"));
|
||||
build.local_rebuild = true;
|
||||
}
|
||||
|
||||
build.verbose(|| println!("finding compilers"));
|
||||
build.verbose(|| eprintln!("finding compilers"));
|
||||
utils::cc_detect::find(&build);
|
||||
// When running `setup`, the profile is about to change, so any requirements we have now may
|
||||
// be different on the next invocation. Don't check for them until the next time x.py is
|
||||
|
|
@ -418,7 +418,7 @@ impl Build {
|
|||
//
|
||||
// Similarly, for `setup` we don't actually need submodules or cargo metadata.
|
||||
if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
|
||||
build.verbose(|| println!("running sanity check"));
|
||||
build.verbose(|| eprintln!("running sanity check"));
|
||||
crate::core::sanity::check(&mut build);
|
||||
|
||||
// Make sure we update these before gathering metadata so we don't get an error about missing
|
||||
|
|
@ -436,7 +436,7 @@ impl Build {
|
|||
// Now, update all existing submodules.
|
||||
build.update_existing_submodules();
|
||||
|
||||
build.verbose(|| println!("learning about cargo"));
|
||||
build.verbose(|| eprintln!("learning about cargo"));
|
||||
crate::core::metadata::build(&mut build);
|
||||
}
|
||||
|
||||
|
|
@ -605,7 +605,7 @@ impl Build {
|
|||
let stamp = dir.join(".stamp");
|
||||
let mut cleared = false;
|
||||
if mtime(&stamp) < mtime(input) {
|
||||
self.verbose(|| println!("Dirty - {}", dir.display()));
|
||||
self.verbose(|| eprintln!("Dirty - {}", dir.display()));
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
cleared = true;
|
||||
} else if stamp.exists() {
|
||||
|
|
@ -890,7 +890,7 @@ impl Build {
|
|||
let executed_at = std::panic::Location::caller();
|
||||
|
||||
self.verbose(|| {
|
||||
println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
|
||||
eprintln!("running: {command:?} (created at {created_at}, executed at {executed_at})")
|
||||
});
|
||||
|
||||
let cmd = command.as_command_mut();
|
||||
|
|
@ -947,7 +947,7 @@ Executed at: {executed_at}"#,
|
|||
|
||||
let fail = |message: &str, output: CommandOutput| -> ! {
|
||||
if self.is_verbose() {
|
||||
println!("{message}");
|
||||
eprintln!("{message}");
|
||||
} else {
|
||||
let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present());
|
||||
// If the command captures output, the user would not see any indication that
|
||||
|
|
@ -957,16 +957,16 @@ Executed at: {executed_at}"#,
|
|||
if let Some(stdout) =
|
||||
output.stdout_if_present().take_if(|s| !s.trim().is_empty())
|
||||
{
|
||||
println!("STDOUT:\n{stdout}\n");
|
||||
eprintln!("STDOUT:\n{stdout}\n");
|
||||
}
|
||||
if let Some(stderr) =
|
||||
output.stderr_if_present().take_if(|s| !s.trim().is_empty())
|
||||
{
|
||||
println!("STDERR:\n{stderr}\n");
|
||||
eprintln!("STDERR:\n{stderr}\n");
|
||||
}
|
||||
println!("Command {command:?} has failed. Rerun with -v to see more details.");
|
||||
eprintln!("Command {command:?} has failed. Rerun with -v to see more details.");
|
||||
} else {
|
||||
println!("Command has failed. Rerun with -v to see more details.");
|
||||
eprintln!("Command has failed. Rerun with -v to see more details.");
|
||||
}
|
||||
}
|
||||
exit!(1);
|
||||
|
|
@ -1011,7 +1011,7 @@ Executed at: {executed_at}"#,
|
|||
match self.config.dry_run {
|
||||
DryRun::SelfCheck => (),
|
||||
DryRun::Disabled | DryRun::UserSelected => {
|
||||
println!("{msg}");
|
||||
eprintln!("{msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1666,7 +1666,7 @@ Executed at: {executed_at}"#,
|
|||
if self.config.dry_run() {
|
||||
return;
|
||||
}
|
||||
self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
|
||||
self.verbose_than(1, || eprintln!("Copy/Link {src:?} to {dst:?}"));
|
||||
if src == dst {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1775,7 +1775,7 @@ Executed at: {executed_at}"#,
|
|||
return;
|
||||
}
|
||||
let dst = dstdir.join(src.file_name().unwrap());
|
||||
self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
|
||||
self.verbose_than(1, || eprintln!("Install {src:?} to {dst:?}"));
|
||||
t!(fs::create_dir_all(dstdir));
|
||||
if !src.exists() {
|
||||
panic!("ERROR: File \"{}\" not found!", src.display());
|
||||
|
|
|
|||
|
|
@ -155,15 +155,15 @@ pub fn find_target(build: &Build, target: TargetSelection) {
|
|||
build.cxx.borrow_mut().insert(target, compiler);
|
||||
}
|
||||
|
||||
build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target)));
|
||||
build.verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple));
|
||||
build.verbose(|| eprintln!("CC_{} = {:?}", target.triple, build.cc(target)));
|
||||
build.verbose(|| eprintln!("CFLAGS_{} = {cflags:?}", target.triple));
|
||||
if let Ok(cxx) = build.cxx(target) {
|
||||
let cxxflags = build.cflags(target, GitRepo::Rustc, CLang::Cxx);
|
||||
build.verbose(|| println!("CXX_{} = {cxx:?}", target.triple));
|
||||
build.verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple));
|
||||
build.verbose(|| eprintln!("CXX_{} = {cxx:?}", target.triple));
|
||||
build.verbose(|| eprintln!("CXXFLAGS_{} = {cxxflags:?}", target.triple));
|
||||
}
|
||||
if let Some(ar) = ar {
|
||||
build.verbose(|| println!("AR_{} = {ar:?}", target.triple));
|
||||
build.verbose(|| eprintln!("AR_{} = {ar:?}", target.triple));
|
||||
build.ar.borrow_mut().insert(target, ar);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -310,4 +310,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
|||
severity: ChangeSeverity::Warning,
|
||||
summary: "Revert `rust.download-rustc` global default to `false` and only use `rust.download-rustc = \"if-unchanged\"` default for library and tools profile. As alt CI rustc is built without debug assertions, `rust.debug-assertions = true` will now inhibit downloading CI rustc.",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 133853,
|
||||
severity: ChangeSeverity::Info,
|
||||
summary: "`build.vendor` is now enabled by default for dist/tarball sources when 'vendor' directory and '.cargo/config.toml' file are present.",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl Drop for TimeIt {
|
|||
fn drop(&mut self) {
|
||||
let time = self.1.elapsed();
|
||||
if !self.0 {
|
||||
println!("\tfinished in {}.{:03} seconds", time.as_secs(), time.subsec_millis());
|
||||
eprintln!("\tfinished in {}.{:03} seconds", time.as_secs(), time.subsec_millis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,12 +267,12 @@ pub fn check_run(cmd: &mut BootstrapCommand, print_cmd_on_fail: bool) -> bool {
|
|||
let status = match cmd.as_command_mut().status() {
|
||||
Ok(status) => status,
|
||||
Err(e) => {
|
||||
println!("failed to execute command: {cmd:?}\nERROR: {e}");
|
||||
eprintln!("failed to execute command: {cmd:?}\nERROR: {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if !status.success() && print_cmd_on_fail {
|
||||
println!(
|
||||
eprintln!(
|
||||
"\n\ncommand did not execute successfully: {cmd:?}\n\
|
||||
expected success, got: {status}\n\n"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ impl BuildMetrics {
|
|||
if version.format_version == CURRENT_FORMAT_VERSION {
|
||||
t!(serde_json::from_slice::<JsonRoot>(&contents)).invocations
|
||||
} else {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: overriding existing build/metrics.json, as it's not \
|
||||
compatible with build metrics format version {CURRENT_FORMAT_VERSION}."
|
||||
);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) ->
|
|||
let cmd = cmd.as_command_mut();
|
||||
cmd.stdout(Stdio::piped());
|
||||
|
||||
builder.verbose(|| println!("running: {cmd:?}"));
|
||||
builder.verbose(|| eprintln!("running: {cmd:?}"));
|
||||
|
||||
let mut process = cmd.spawn().unwrap();
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) ->
|
|||
|
||||
let result = process.wait_with_output().unwrap();
|
||||
if !result.status.success() && builder.is_verbose() {
|
||||
println!(
|
||||
eprintln!(
|
||||
"\n\ncommand did not execute successfully: {cmd:?}\n\
|
||||
expected success, got: {}",
|
||||
result.status
|
||||
|
|
@ -135,7 +135,9 @@ impl<'a> Renderer<'a> {
|
|||
if self.up_to_date_tests > 0 {
|
||||
let n = self.up_to_date_tests;
|
||||
let s = if n > 1 { "s" } else { "" };
|
||||
println!("help: ignored {n} up-to-date test{s}; use `--force-rerun` to prevent this\n");
|
||||
eprintln!(
|
||||
"help: ignored {n} up-to-date test{s}; use `--force-rerun` to prevent this\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,12 +187,12 @@ impl<'a> Renderer<'a> {
|
|||
}
|
||||
|
||||
fn render_test_outcome_verbose(&self, outcome: Outcome<'_>, test: &TestOutcome) {
|
||||
print!("test {} ... ", test.name);
|
||||
self.builder.colored_stdout(|stdout| outcome.write_long(stdout)).unwrap();
|
||||
eprint!("test {} ... ", test.name);
|
||||
self.builder.colored_stderr(|stdout| outcome.write_long(stdout)).unwrap();
|
||||
if let Some(exec_time) = test.exec_time {
|
||||
print!(" ({exec_time:.2?})");
|
||||
eprint!(" ({exec_time:.2?})");
|
||||
}
|
||||
println!();
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, test: &TestOutcome) {
|
||||
|
|
@ -198,45 +200,45 @@ impl<'a> Renderer<'a> {
|
|||
if let Some(total) = self.tests_count {
|
||||
let total = total.to_string();
|
||||
let executed = format!("{:>width$}", self.executed_tests - 1, width = total.len());
|
||||
print!(" {executed}/{total}");
|
||||
eprint!(" {executed}/{total}");
|
||||
}
|
||||
println!();
|
||||
eprintln!();
|
||||
self.terse_tests_in_line = 0;
|
||||
}
|
||||
|
||||
self.terse_tests_in_line += 1;
|
||||
self.builder.colored_stdout(|stdout| outcome.write_short(stdout, &test.name)).unwrap();
|
||||
self.builder.colored_stderr(|stdout| outcome.write_short(stdout, &test.name)).unwrap();
|
||||
let _ = std::io::stdout().flush();
|
||||
}
|
||||
|
||||
fn render_suite_outcome(&self, outcome: Outcome<'_>, suite: &SuiteOutcome) {
|
||||
// The terse output doesn't end with a newline, so we need to add it ourselves.
|
||||
if !self.builder.config.verbose_tests {
|
||||
println!();
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
if !self.failures.is_empty() {
|
||||
println!("\nfailures:\n");
|
||||
eprintln!("\nfailures:\n");
|
||||
for failure in &self.failures {
|
||||
if failure.stdout.is_some() || failure.message.is_some() {
|
||||
println!("---- {} stdout ----", failure.name);
|
||||
eprintln!("---- {} stdout ----", failure.name);
|
||||
if let Some(stdout) = &failure.stdout {
|
||||
println!("{stdout}");
|
||||
eprintln!("{stdout}");
|
||||
}
|
||||
if let Some(message) = &failure.message {
|
||||
println!("NOTE: {message}");
|
||||
eprintln!("NOTE: {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nfailures:");
|
||||
eprintln!("\nfailures:");
|
||||
for failure in &self.failures {
|
||||
println!(" {}", failure.name);
|
||||
eprintln!(" {}", failure.name);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.benches.is_empty() {
|
||||
println!("\nbenchmarks:");
|
||||
eprintln!("\nbenchmarks:");
|
||||
|
||||
let mut rows = Vec::new();
|
||||
for bench in &self.benches {
|
||||
|
|
@ -251,13 +253,13 @@ impl<'a> Renderer<'a> {
|
|||
let max_1 = rows.iter().map(|r| r.1.len()).max().unwrap_or(0);
|
||||
let max_2 = rows.iter().map(|r| r.2.len()).max().unwrap_or(0);
|
||||
for row in &rows {
|
||||
println!(" {:<max_0$} {:>max_1$} {:>max_2$}", row.0, row.1, row.2);
|
||||
eprintln!(" {:<max_0$} {:>max_1$} {:>max_2$}", row.0, row.1, row.2);
|
||||
}
|
||||
}
|
||||
|
||||
print!("\ntest result: ");
|
||||
self.builder.colored_stdout(|stdout| outcome.write_long(stdout)).unwrap();
|
||||
println!(
|
||||
eprint!("\ntest result: ");
|
||||
self.builder.colored_stderr(|stdout| outcome.write_long(stdout)).unwrap();
|
||||
eprintln!(
|
||||
". {} passed; {} failed; {} ignored; {} measured; {} filtered out{time}\n",
|
||||
suite.passed,
|
||||
suite.failed,
|
||||
|
|
@ -274,7 +276,7 @@ impl<'a> Renderer<'a> {
|
|||
fn render_message(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::Suite(SuiteMessage::Started { test_count }) => {
|
||||
println!("\nrunning {test_count} tests");
|
||||
eprintln!("\nrunning {test_count} tests");
|
||||
self.executed_tests = 0;
|
||||
self.terse_tests_in_line = 0;
|
||||
self.tests_count = Some(test_count);
|
||||
|
|
@ -314,7 +316,7 @@ impl<'a> Renderer<'a> {
|
|||
self.failures.push(outcome);
|
||||
}
|
||||
Message::Test(TestMessage::Timeout { name }) => {
|
||||
println!("test {name} has been running for a long time");
|
||||
eprintln!("test {name} has been running for a long time");
|
||||
}
|
||||
Message::Test(TestMessage::Started) => {} // Not useful
|
||||
}
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ impl<'a> Tarball<'a> {
|
|||
// For `x install` tarball files aren't needed, so we can speed up the process by not producing them.
|
||||
let compression_profile = if self.builder.kind == Kind::Install {
|
||||
self.builder.verbose(|| {
|
||||
println!("Forcing dist.compression-profile = 'no-op' for `x install`.")
|
||||
eprintln!("Forcing dist.compression-profile = 'no-op' for `x install`.")
|
||||
});
|
||||
// "no-op" indicates that the rust-installer won't produce compressed tarball sources.
|
||||
"no-op"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ RUN sh /scripts/rustbuild-setup.sh
|
|||
WORKDIR /tmp
|
||||
|
||||
COPY scripts/crosstool-ng-build.sh /scripts/
|
||||
COPY host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig
|
||||
COPY host-aarch64/dist-arm-linux/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig
|
||||
RUN /scripts/crosstool-ng-build.sh
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
|
|
@ -43,7 +43,7 @@ runners:
|
|||
os: windows-2022-16core-64gb
|
||||
<<: *base-job
|
||||
|
||||
- &job-aarch64-linux
|
||||
- &job-linux-8c-aarch64
|
||||
os: ubuntu-22.04-arm64-8core-32gb
|
||||
|
||||
envs:
|
||||
|
|
@ -139,10 +139,10 @@ auto:
|
|||
#############################
|
||||
|
||||
- image: aarch64-gnu
|
||||
<<: *job-aarch64-linux
|
||||
<<: *job-linux-8c-aarch64
|
||||
|
||||
- image: aarch64-gnu-debug
|
||||
<<: *job-aarch64-linux
|
||||
<<: *job-linux-8c-aarch64
|
||||
|
||||
- image: arm-android
|
||||
<<: *job-linux-4c
|
||||
|
|
@ -159,7 +159,7 @@ auto:
|
|||
<<: *job-linux-4c
|
||||
|
||||
- image: dist-arm-linux
|
||||
<<: *job-linux-8c
|
||||
<<: *job-linux-8c-aarch64
|
||||
|
||||
- image: dist-armhf-linux
|
||||
<<: *job-linux-4c
|
||||
|
|
|
|||
|
|
@ -1841,9 +1841,6 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
|
|||
TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
|
||||
// Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
|
||||
TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer,
|
||||
TyKind::AnonAdt(..) => {
|
||||
unimplemented!("Anonymous structs or unions are not supported yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
|
|
@ -285,7 +285,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
|
|||
/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
|
||||
fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Struct(_, _, Some(base)) = parent.kind
|
||||
&& let ExprKind::Struct(_, _, StructTailExpr::Base(base)) = parent.kind
|
||||
{
|
||||
base.hir_id == expr.hir_id
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
|
|||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
|
||||
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
|
||||
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
|
||||
|
|
@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
// Visit base with no bound.
|
||||
if let Some(base) = base {
|
||||
if let StructTailExpr::Base(base) = base {
|
||||
self.ty_bounds.push(ExplicitTyBound(false));
|
||||
self.visit_expr(base);
|
||||
self.ty_bounds.pop();
|
||||
|
|
|
|||
|
|
@ -818,7 +818,6 @@ impl TyCoercionStability {
|
|||
| TyKind::Typeof(..)
|
||||
| TyKind::TraitObject(..)
|
||||
| TyKind::InferDelegation(..)
|
||||
| TyKind::AnonAdt(..)
|
||||
| TyKind::Err(_) => Self::Reborrow,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::fulfill_or_allowed;
|
|||
use clippy_utils::source::snippet;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_hir::{self as hir, ExprKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
|
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
}
|
||||
fields_snippet.push_str(&last_ident.to_string());
|
||||
|
||||
let base_snippet = if let Some(base) = base {
|
||||
let base_snippet = if let StructTailExpr::Base(base) = base {
|
||||
format!(", ..{}", snippet(cx, base.span, ".."))
|
||||
} else {
|
||||
String::new()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
|
@ -43,7 +43,7 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
|
||||
if let ExprKind::Struct(path, fields @ [field, ..], StructTailExpr::None) = e.kind
|
||||
// If the first character of any field is a digit it has to be a tuple.
|
||||
&& field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
|
||||
// Type aliases can't be used as functions.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::higher::ForLoop;
|
|||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
|
||||
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::iter::once;
|
||||
|
|
@ -164,7 +164,7 @@ fn never_loop_expr<'tcx>(
|
|||
},
|
||||
ExprKind::Struct(_, fields, base) => {
|
||||
let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
|
||||
if let Some(base) = base {
|
||||
if let StructTailExpr::Base(base) = base {
|
||||
combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
|
||||
} else {
|
||||
fields
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
cx.param_env,
|
||||
ty,
|
||||
traits::ObligationCause::dummy_with_span(span),
|
||||
rustc_hir::Safety::Safe,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -51,7 +51,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Struct(_, fields, Some(base)) = expr.kind {
|
||||
if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if fields.len() == def.non_enum_variant().fields.len()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{
|
||||
BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
|
||||
UnsafeSource, is_range_literal,
|
||||
UnsafeSource, StructTailExpr, is_range_literal,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
ExprKind::Struct(_, fields, ref base) => {
|
||||
!has_drop(cx, cx.typeck_results().expr_ty(expr))
|
||||
&& fields.iter().all(|field| has_no_effect(cx, field.expr))
|
||||
&& base.as_ref().is_none_or(|base| has_no_effect(cx, base))
|
||||
&& match &base {
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => true,
|
||||
StructTailExpr::Base(base) => has_no_effect(cx, base),
|
||||
}
|
||||
},
|
||||
ExprKind::Call(callee, args) => {
|
||||
if let ExprKind::Path(ref qpath) = callee.kind {
|
||||
|
|
@ -342,6 +345,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
|
|||
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
|
||||
None
|
||||
} else {
|
||||
let base = match base {
|
||||
StructTailExpr::Base(base) => Some(base),
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
|
||||
};
|
||||
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_hir::{
|
|||
BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -302,7 +302,10 @@ impl<'tcx> NonCopyConst<'tcx> {
|
|||
tcx.const_eval_global_id_for_typeck(typing_env, cid, span)
|
||||
},
|
||||
Ok(None) => Err(ErrorHandled::TooGeneric(span)),
|
||||
Err(err) => Err(ErrorHandled::Reported(err.into(), span)),
|
||||
Err(err) => Err(ErrorHandled::Reported(
|
||||
ReportedErrorInfo::non_const_eval_error(err),
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
|
|||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_ast::{LitIntType, LitKind, UintTy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
|
@ -86,7 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
|
|||
return;
|
||||
};
|
||||
|
||||
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], None) = inner_expr.kind else {
|
||||
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{get_parent_expr, path_to_local};
|
||||
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp};
|
||||
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
|
|
@ -59,15 +59,15 @@ impl LateLintPass<'_> for UnnecessaryStruct {
|
|||
let field_path = same_path_in_all_fields(cx, expr, fields);
|
||||
|
||||
let sugg = match (field_path, base) {
|
||||
(Some(&path), None) => {
|
||||
(Some(&path), StructTailExpr::None | StructTailExpr::DefaultFields(_)) => {
|
||||
// all fields match, no base given
|
||||
path.span
|
||||
},
|
||||
(Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
|
||||
(Some(path), StructTailExpr::Base(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
|
||||
// all fields match, has base: ensure that the path of the base matches
|
||||
base.span
|
||||
},
|
||||
(None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
|
||||
(None, StructTailExpr::Base(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
|
||||
// just the base, no explicit fields
|
||||
base.span
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind,
|
||||
ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
|
||||
ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -598,7 +598,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
},
|
||||
ExprKind::Struct(qpath, fields, base) => {
|
||||
bind!(self, qpath, fields);
|
||||
opt_bind!(self, base);
|
||||
let base = OptionPat::new(match base {
|
||||
StructTailExpr::Base(base) => Some(self.bind("base", base)),
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
|
||||
});
|
||||
kind!("Struct({qpath}, {fields}, {base})");
|
||||
self.qpath(qpath);
|
||||
self.slice(fields, |field| {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item;
|
|||
|
||||
use rustc_ast::ast;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, StructTailExpr, HirId, LoopSource, MatchSource, Node, Pat, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym, symbol};
|
||||
|
||||
|
|
@ -236,7 +236,7 @@ impl<'a> Range<'a> {
|
|||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
},
|
||||
ExprKind::Struct(path, fields, None) => match (path, fields) {
|
||||
ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
|
||||
(QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
|
||||
start: None,
|
||||
end: None,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_hir::{
|
|||
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
|
||||
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
|
||||
LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
|
||||
TyKind,
|
||||
TyKind, StructTailExpr,
|
||||
};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -380,7 +380,12 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
(ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
|
||||
(&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
|
||||
self.eq_qpath(l_path, r_path)
|
||||
&& both(lo.as_ref(), ro.as_ref(), |l, r| self.eq_expr(l, r))
|
||||
&& match (lo, ro) {
|
||||
(StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
|
||||
(StructTailExpr::None, StructTailExpr::None) => true,
|
||||
(StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
&& over(lf, rf, |l, r| self.eq_expr_field(l, r))
|
||||
},
|
||||
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
|
||||
|
|
@ -591,7 +596,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
(TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
|
||||
(&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
|
||||
(&TyKind::Infer, &TyKind::Infer) => true,
|
||||
(TyKind::AnonAdt(l_item_id), TyKind::AnonAdt(r_item_id)) => l_item_id == r_item_id,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1017,7 +1021,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(f.expr);
|
||||
}
|
||||
|
||||
if let Some(e) = *expr {
|
||||
if let StructTailExpr::Base(e) = *expr {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
},
|
||||
|
|
@ -1241,8 +1245,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
| TyKind::Infer
|
||||
| TyKind::Never
|
||||
| TyKind::InferDelegation(..)
|
||||
| TyKind::OpaqueDef(_)
|
||||
| TyKind::AnonAdt(_) => {},
|
||||
| TyKind::OpaqueDef(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
|
|||
use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
|
||||
use rustc_hir::{
|
||||
AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
|
||||
Safety, Stmt, UnOp, UnsafeSource,
|
||||
Safety, Stmt, UnOp, UnsafeSource, StructTailExpr,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -663,7 +663,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
|
|||
for field in fields {
|
||||
helper(typeck, true, field.expr, f)?;
|
||||
}
|
||||
if let Some(default) = default {
|
||||
if let StructTailExpr::Base(default) = default {
|
||||
helper(typeck, false, default, f)?;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ where
|
|||
}
|
||||
|
||||
if !wrote_data {
|
||||
println!("note: diff is identical to nightly rustdoc");
|
||||
eprintln!("note: diff is identical to nightly rustdoc");
|
||||
assert!(diff_output.metadata().unwrap().len() == 0);
|
||||
return false;
|
||||
} else if verbose {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
|
|||
}
|
||||
|
||||
if config.remote_test_client.is_some() && !config.target.contains("android") {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: debuginfo tests are not available when \
|
||||
testing with remote"
|
||||
);
|
||||
|
|
@ -28,7 +28,7 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
|
|||
}
|
||||
|
||||
if config.target.contains("android") {
|
||||
println!(
|
||||
eprintln!(
|
||||
"{} debug-info test uses tcp 5039 port.\
|
||||
please reserve it",
|
||||
config.target
|
||||
|
|
@ -50,7 +50,7 @@ pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
|
|||
config.lldb_python_dir.as_ref()?;
|
||||
|
||||
if let Some(350) = config.lldb_version {
|
||||
println!(
|
||||
eprintln!(
|
||||
"WARNING: The used version of LLDB (350) has a \
|
||||
known issue that breaks debuginfo tests. See \
|
||||
issue #32520 for more information. Skipping all \
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
let (argv0, args_) = args.split_first().unwrap();
|
||||
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
|
||||
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
||||
println!("{}", opts.usage(&message));
|
||||
println!();
|
||||
eprintln!("{}", opts.usage(&message));
|
||||
eprintln!();
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
|
@ -200,8 +200,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
|
||||
if matches.opt_present("h") || matches.opt_present("help") {
|
||||
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
||||
println!("{}", opts.usage(&message));
|
||||
println!();
|
||||
eprintln!("{}", opts.usage(&message));
|
||||
eprintln!();
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
|
@ -508,7 +508,7 @@ pub fn run_tests(config: Arc<Config>) {
|
|||
// easy to miss which tests failed, and as such fail to reproduce
|
||||
// the failure locally.
|
||||
|
||||
println!(
|
||||
eprintln!(
|
||||
"Some tests failed in compiletest suite={}{} mode={} host={} target={}",
|
||||
config.suite,
|
||||
config
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
|
|||
|
||||
if config.verbose {
|
||||
// We're going to be dumping a lot of info. Start on a new line.
|
||||
print!("\n\n");
|
||||
eprintln!("\n");
|
||||
}
|
||||
debug!("running {:?}", testpaths.file.display());
|
||||
let mut props = TestProps::from_file(&testpaths.file, revision, &config);
|
||||
|
|
@ -353,7 +353,7 @@ impl<'test> TestCx<'test> {
|
|||
{
|
||||
self.error(&format!("{} test did not emit an error", self.config.mode));
|
||||
if self.config.mode == crate::common::Mode::Ui {
|
||||
println!("note: by default, ui tests are expected not to compile");
|
||||
eprintln!("note: by default, ui tests are expected not to compile");
|
||||
}
|
||||
proc_res.fatal(None, || ());
|
||||
};
|
||||
|
|
@ -774,20 +774,20 @@ impl<'test> TestCx<'test> {
|
|||
unexpected.len(),
|
||||
not_found.len()
|
||||
));
|
||||
println!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
|
||||
eprintln!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
|
||||
if !unexpected.is_empty() {
|
||||
println!("{}", "--- unexpected errors (from JSON output) ---".green());
|
||||
eprintln!("{}", "--- unexpected errors (from JSON output) ---".green());
|
||||
for error in &unexpected {
|
||||
println!("{}", error.render_for_expected());
|
||||
eprintln!("{}", error.render_for_expected());
|
||||
}
|
||||
println!("{}", "---".green());
|
||||
eprintln!("{}", "---".green());
|
||||
}
|
||||
if !not_found.is_empty() {
|
||||
println!("{}", "--- not found errors (from test file) ---".red());
|
||||
eprintln!("{}", "--- not found errors (from test file) ---".red());
|
||||
for error in ¬_found {
|
||||
println!("{}", error.render_for_expected());
|
||||
eprintln!("{}", error.render_for_expected());
|
||||
}
|
||||
println!("{}", "---\n".red());
|
||||
eprintln!("{}", "---\n".red());
|
||||
}
|
||||
panic!("errors differ from expected");
|
||||
}
|
||||
|
|
@ -1876,18 +1876,18 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
|
||||
if self.config.verbose {
|
||||
println!("------stdout------------------------------");
|
||||
println!("{}", out);
|
||||
println!("------stderr------------------------------");
|
||||
println!("{}", err);
|
||||
println!("------------------------------------------");
|
||||
eprintln!("------stdout------------------------------");
|
||||
eprintln!("{}", out);
|
||||
eprintln!("------stderr------------------------------");
|
||||
eprintln!("{}", err);
|
||||
eprintln!("------------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&self, err: &str) {
|
||||
match self.revision {
|
||||
Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
|
||||
None => println!("\nerror: {}", err),
|
||||
Some(rev) => eprintln!("\nerror in revision `{}`: {}", rev, err),
|
||||
None => eprintln!("\nerror: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1972,7 +1972,7 @@ impl<'test> TestCx<'test> {
|
|||
if !self.config.has_html_tidy {
|
||||
return;
|
||||
}
|
||||
println!("info: generating a diff against nightly rustdoc");
|
||||
eprintln!("info: generating a diff against nightly rustdoc");
|
||||
|
||||
let suffix =
|
||||
self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
|
||||
|
|
@ -2082,7 +2082,7 @@ impl<'test> TestCx<'test> {
|
|||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
} else {
|
||||
use colored::Colorize;
|
||||
|
|
@ -2496,7 +2496,7 @@ impl<'test> TestCx<'test> {
|
|||
)"#
|
||||
)
|
||||
.replace_all(&output, |caps: &Captures<'_>| {
|
||||
println!("{}", &caps[0]);
|
||||
eprintln!("{}", &caps[0]);
|
||||
caps[0].replace(r"\", "/")
|
||||
})
|
||||
.replace("\r\n", "\n")
|
||||
|
|
@ -2601,14 +2601,14 @@ impl<'test> TestCx<'test> {
|
|||
if let Err(err) = fs::write(&actual_path, &actual) {
|
||||
self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",));
|
||||
}
|
||||
println!("Saved the actual {stream} to {actual_path:?}");
|
||||
eprintln!("Saved the actual {stream} to {actual_path:?}");
|
||||
|
||||
let expected_path =
|
||||
expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);
|
||||
|
||||
if !self.config.bless {
|
||||
if expected.is_empty() {
|
||||
println!("normalized {}:\n{}\n", stream, actual);
|
||||
eprintln!("normalized {}:\n{}\n", stream, actual);
|
||||
} else {
|
||||
self.show_diff(
|
||||
stream,
|
||||
|
|
@ -2631,10 +2631,10 @@ impl<'test> TestCx<'test> {
|
|||
if let Err(err) = fs::write(&expected_path, &actual) {
|
||||
self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}"));
|
||||
}
|
||||
println!("Blessing the {stream} of {test_name} in {expected_path:?}");
|
||||
eprintln!("Blessing the {stream} of {test_name} in {expected_path:?}");
|
||||
}
|
||||
|
||||
println!("\nThe actual {0} differed from the expected {0}.", stream);
|
||||
eprintln!("\nThe actual {0} differed from the expected {0}.", stream);
|
||||
|
||||
if self.config.bless { 0 } else { 1 }
|
||||
}
|
||||
|
|
@ -2783,7 +2783,7 @@ impl<'test> TestCx<'test> {
|
|||
fs::create_dir_all(&incremental_dir).unwrap();
|
||||
|
||||
if self.config.verbose {
|
||||
println!("init_incremental_test: incremental_dir={}", incremental_dir.display());
|
||||
eprintln!("init_incremental_test: incremental_dir={}", incremental_dir.display());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2841,7 +2841,7 @@ impl ProcRes {
|
|||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
eprintln!(
|
||||
"status: {}\ncommand: {}\n{}\n{}\n",
|
||||
self.status,
|
||||
self.cmdline,
|
||||
|
|
@ -2852,7 +2852,7 @@ impl ProcRes {
|
|||
|
||||
pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
|
||||
if let Some(e) = err {
|
||||
println!("\nerror: {}", e);
|
||||
eprintln!("\nerror: {}", e);
|
||||
}
|
||||
self.print_info();
|
||||
on_failure();
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ impl TestCx<'_> {
|
|||
if !missing.is_empty() {
|
||||
missing.sort();
|
||||
|
||||
println!("\nThese items should have been contained but were not:\n");
|
||||
eprintln!("\nThese items should have been contained but were not:\n");
|
||||
|
||||
for item in &missing {
|
||||
println!("{}", item);
|
||||
eprintln!("{}", item);
|
||||
}
|
||||
|
||||
println!("\n");
|
||||
eprintln!("\n");
|
||||
}
|
||||
|
||||
if !unexpected.is_empty() {
|
||||
|
|
@ -80,24 +80,24 @@ impl TestCx<'_> {
|
|||
sorted
|
||||
};
|
||||
|
||||
println!("\nThese items were contained but should not have been:\n");
|
||||
eprintln!("\nThese items were contained but should not have been:\n");
|
||||
|
||||
for item in sorted {
|
||||
println!("{}", item);
|
||||
eprintln!("{}", item);
|
||||
}
|
||||
|
||||
println!("\n");
|
||||
eprintln!("\n");
|
||||
}
|
||||
|
||||
if !wrong_cgus.is_empty() {
|
||||
wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
|
||||
println!("\nThe following items were assigned to wrong codegen units:\n");
|
||||
eprintln!("\nThe following items were assigned to wrong codegen units:\n");
|
||||
|
||||
for &(ref expected_item, ref actual_item) in &wrong_cgus {
|
||||
println!("{}", expected_item.name);
|
||||
println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
|
||||
println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
|
||||
println!();
|
||||
eprintln!("{}", expected_item.name);
|
||||
eprintln!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
|
||||
eprintln!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ impl TestCx<'_> {
|
|||
cmdline,
|
||||
};
|
||||
if adb.kill().is_err() {
|
||||
println!("Adb process is already finished.");
|
||||
eprintln!("Adb process is already finished.");
|
||||
}
|
||||
} else {
|
||||
let rust_src_root =
|
||||
|
|
@ -275,7 +275,7 @@ impl TestCx<'_> {
|
|||
|
||||
match self.config.gdb_version {
|
||||
Some(version) => {
|
||||
println!("NOTE: compiletest thinks it is using GDB version {}", version);
|
||||
eprintln!("NOTE: compiletest thinks it is using GDB version {}", version);
|
||||
|
||||
if version > extract_gdb_version("7.4").unwrap() {
|
||||
// Add the directory containing the pretty printers to
|
||||
|
|
@ -297,7 +297,7 @@ impl TestCx<'_> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
eprintln!(
|
||||
"NOTE: compiletest does not know which version of \
|
||||
GDB it is using"
|
||||
);
|
||||
|
|
@ -392,10 +392,10 @@ impl TestCx<'_> {
|
|||
|
||||
match self.config.lldb_version {
|
||||
Some(ref version) => {
|
||||
println!("NOTE: compiletest thinks it is using LLDB version {}", version);
|
||||
eprintln!("NOTE: compiletest thinks it is using LLDB version {}", version);
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
eprintln!(
|
||||
"NOTE: compiletest does not know which version of \
|
||||
LLDB it is using"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl TestCx<'_> {
|
|||
assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
|
||||
|
||||
if self.config.verbose {
|
||||
print!("revision={:?} props={:#?}", revision, self.props);
|
||||
eprint!("revision={:?} props={:#?}", revision, self.props);
|
||||
}
|
||||
|
||||
if revision.starts_with("cpass") {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ impl TestCx<'_> {
|
|||
}
|
||||
let expected_string = fs::read_to_string(&expected_file).unwrap();
|
||||
if dumped_string != expected_string {
|
||||
print!("{}", write_diff(&expected_string, &dumped_string, 3));
|
||||
eprint!("{}", write_diff(&expected_string, &dumped_string, 3));
|
||||
panic!(
|
||||
"Actual MIR output differs from expected MIR output {}",
|
||||
expected_file.display()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl TestCx<'_> {
|
|||
|
||||
if !res.status.success() {
|
||||
self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
|
||||
println!("Rustdoc Output:");
|
||||
eprintln!("Rustdoc Output:");
|
||||
proc_res.print_info();
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,10 +109,10 @@ impl TestCx<'_> {
|
|||
}
|
||||
|
||||
if errors > 0 {
|
||||
println!("To update references, rerun the tests and pass the `--bless` flag");
|
||||
eprintln!("To update references, rerun the tests and pass the `--bless` flag");
|
||||
let relative_path_to_file =
|
||||
self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
|
||||
println!(
|
||||
eprintln!(
|
||||
"To only update this specific test, also pass `--test-args {}`",
|
||||
relative_path_to_file.display(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fn path_div() -> &'static str {
|
|||
pub fn logv(config: &Config, s: String) {
|
||||
debug!("{}", s);
|
||||
if config.verbose {
|
||||
println!("{}", s);
|
||||
eprintln!("{}", s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ impl Cache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &Value {
|
||||
&self.value
|
||||
// FIXME: Make this failible, so jsonpath syntax error has line number.
|
||||
pub fn select(&self, path: &str) -> Vec<&Value> {
|
||||
jsonpath_lib::select(&self.value, path).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,7 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CkError {
|
||||
/// A check failed. File didn't exist or failed to match the command
|
||||
FailedCheck(String, Command),
|
||||
/// An error triggered by some other error
|
||||
Induced(Box<dyn Error>),
|
||||
}
|
||||
|
||||
impl fmt::Display for CkError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
CkError::FailedCheck(msg, cmd) => {
|
||||
write!(f, "Failed check: {} on line {}", msg, cmd.lineno)
|
||||
}
|
||||
CkError::Induced(err) => write!(f, "Check failed: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Error + 'static> From<T> for CkError {
|
||||
fn from(err: T) -> CkError {
|
||||
CkError::Induced(Box::new(err))
|
||||
}
|
||||
pub struct CkError {
|
||||
pub message: String,
|
||||
pub command: Command,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::OnceLock;
|
||||
use std::{env, fmt, fs};
|
||||
use std::{env, fs};
|
||||
|
||||
use jsonpath_lib::select;
|
||||
use regex::{Regex, RegexBuilder};
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -14,90 +14,134 @@ use cache::Cache;
|
|||
use config::parse_config;
|
||||
use error::CkError;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
fn main() -> ExitCode {
|
||||
let config = parse_config(env::args().collect());
|
||||
|
||||
let mut failed = Vec::new();
|
||||
let mut cache = Cache::new(&config);
|
||||
let commands = get_commands(&config.template)
|
||||
.map_err(|_| format!("Jsondocck failed for {}", &config.template))?;
|
||||
let Ok(commands) = get_commands(&config.template) else {
|
||||
eprintln!("Jsondocck failed for {}", &config.template);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
|
||||
for command in commands {
|
||||
if let Err(e) = check_command(command, &mut cache) {
|
||||
failed.push(e);
|
||||
if let Err(message) = check_command(&command, &mut cache) {
|
||||
failed.push(CkError { command, message });
|
||||
}
|
||||
}
|
||||
|
||||
if failed.is_empty() {
|
||||
Ok(())
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
for i in failed {
|
||||
eprintln!("{}", i);
|
||||
eprintln!("{}:{}, command failed", config.template, i.command.lineno);
|
||||
eprintln!("{}", i.message)
|
||||
}
|
||||
Err(format!("Jsondocck failed for {}", &config.template))
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Command {
|
||||
negated: bool,
|
||||
kind: CommandKind,
|
||||
args: Vec<String>,
|
||||
path: String,
|
||||
lineno: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CommandKind {
|
||||
Has,
|
||||
Count,
|
||||
Is,
|
||||
IsMany,
|
||||
Set,
|
||||
enum CommandKind {
|
||||
/// `//@ has <path>`
|
||||
///
|
||||
/// Checks the path exists.
|
||||
HasPath,
|
||||
|
||||
/// `//@ has <path> <value>`
|
||||
///
|
||||
/// Check one thing at the path is equal to the value.
|
||||
HasValue { value: String },
|
||||
|
||||
/// `//@ !has <path>`
|
||||
///
|
||||
/// Checks the path doesn't exist.
|
||||
HasNotPath,
|
||||
|
||||
/// `//@ is <path> <value>`
|
||||
///
|
||||
/// Check the path is the given value.
|
||||
Is { value: String },
|
||||
|
||||
/// `//@ is <path> <value> <value>...`
|
||||
///
|
||||
/// Check that the path matches to exactly every given value.
|
||||
IsMany { values: Vec<String> },
|
||||
|
||||
/// `//@ !is <path> <value>`
|
||||
///
|
||||
/// Check the path isn't the given value.
|
||||
IsNot { value: String },
|
||||
|
||||
/// `//@ count <path> <value>`
|
||||
///
|
||||
/// Check the path has the expected number of matches.
|
||||
CountIs { expected: usize },
|
||||
|
||||
/// `//@ set <name> = <path>`
|
||||
Set { variable: String },
|
||||
}
|
||||
|
||||
impl CommandKind {
|
||||
fn validate(&self, args: &[String], lineno: usize) -> bool {
|
||||
// FIXME(adotinthevoid): We should "parse, don't validate" here, so we avoid ad-hoc
|
||||
// indexing in check_command.
|
||||
let count = match self {
|
||||
CommandKind::Has => (1..=2).contains(&args.len()),
|
||||
CommandKind::IsMany => args.len() >= 2,
|
||||
CommandKind::Count | CommandKind::Is => 2 == args.len(),
|
||||
CommandKind::Set => 3 == args.len(),
|
||||
};
|
||||
|
||||
if !count {
|
||||
print_err(&format!("Incorrect number of arguments to `{}`", self), lineno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if let CommandKind::Count = self {
|
||||
if args[1].parse::<usize>().is_err() {
|
||||
print_err(
|
||||
&format!(
|
||||
"Second argument to `count` must be a valid usize (got `{}`)",
|
||||
args[1]
|
||||
),
|
||||
lineno,
|
||||
);
|
||||
return false;
|
||||
/// Returns both the kind and the path.
|
||||
///
|
||||
/// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
|
||||
fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> {
|
||||
let kind = match (command_name, negated) {
|
||||
("count", false) => {
|
||||
assert_eq!(args.len(), 2);
|
||||
let expected = args[1].parse().expect("invalid number for `count`");
|
||||
Self::CountIs { expected }
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
("ismany", false) => {
|
||||
// FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
|
||||
assert!(args.len() >= 2, "Not enough args to `ismany`");
|
||||
let values = args[1..].to_owned();
|
||||
Self::IsMany { values }
|
||||
}
|
||||
|
||||
impl fmt::Display for CommandKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let text = match self {
|
||||
CommandKind::Has => "has",
|
||||
CommandKind::IsMany => "ismany",
|
||||
CommandKind::Count => "count",
|
||||
CommandKind::Is => "is",
|
||||
CommandKind::Set => "set",
|
||||
("is", false) => {
|
||||
assert_eq!(args.len(), 2);
|
||||
Self::Is { value: args[1].clone() }
|
||||
}
|
||||
("is", true) => {
|
||||
assert_eq!(args.len(), 2);
|
||||
Self::IsNot { value: args[1].clone() }
|
||||
}
|
||||
|
||||
("set", false) => {
|
||||
assert_eq!(args.len(), 3);
|
||||
assert_eq!(args[1], "=");
|
||||
return Some((Self::Set { variable: args[0].clone() }, &args[2]));
|
||||
}
|
||||
|
||||
("has", false) => match args {
|
||||
[_path] => Self::HasPath,
|
||||
[_path, value] => Self::HasValue { value: value.clone() },
|
||||
_ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
|
||||
},
|
||||
("has", true) => {
|
||||
assert_eq!(args.len(), 1, "args={args:?}");
|
||||
Self::HasNotPath
|
||||
}
|
||||
|
||||
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
|
||||
}
|
||||
};
|
||||
write!(f, "{}", text)
|
||||
|
||||
Some((kind, &args[0]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,8 +169,7 @@ fn print_err(msg: &str, lineno: usize) {
|
|||
// See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
|
||||
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
|
||||
|
||||
/// Get a list of commands from a file. Does the work of ensuring the commands
|
||||
/// are syntactically valid.
|
||||
/// Get a list of commands from a file.
|
||||
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
||||
let mut commands = Vec::new();
|
||||
let mut errors = false;
|
||||
|
|
@ -142,217 +185,102 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
|||
|
||||
let negated = cap.name("negated").unwrap().as_str() == "!";
|
||||
|
||||
let cmd = match cap.name("cmd").unwrap().as_str() {
|
||||
"has" => CommandKind::Has,
|
||||
"count" => CommandKind::Count,
|
||||
"is" => CommandKind::Is,
|
||||
"ismany" => CommandKind::IsMany,
|
||||
"set" => CommandKind::Set,
|
||||
// FIXME: See the comment above the `include!(...)`.
|
||||
cmd if KNOWN_DIRECTIVE_NAMES.contains(&cmd) => continue,
|
||||
cmd => {
|
||||
print_err(&format!("Unrecognized command name `{cmd}`"), lineno);
|
||||
errors = true;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));
|
||||
|
||||
let args = match args {
|
||||
let args_str = &cap["args"];
|
||||
let args = match shlex::split(args_str) {
|
||||
Some(args) => args,
|
||||
None => {
|
||||
print_err(
|
||||
&format!(
|
||||
"Invalid arguments to shlex::split: `{}`",
|
||||
cap.name("args").unwrap().as_str()
|
||||
),
|
||||
lineno,
|
||||
);
|
||||
print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
|
||||
errors = true;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if !cmd.validate(&args, lineno) {
|
||||
errors = true;
|
||||
continue;
|
||||
if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
|
||||
commands.push(Command { kind, lineno, path: path.to_owned() })
|
||||
}
|
||||
|
||||
commands.push(Command { negated, kind: cmd, args, lineno })
|
||||
}
|
||||
|
||||
if !errors { Ok(commands) } else { Err(()) }
|
||||
}
|
||||
|
||||
/// Performs the actual work of ensuring a command passes. Generally assumes the command
|
||||
/// is syntactically valid.
|
||||
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
||||
// FIXME: Be more granular about why, (e.g. syntax error, count not equal)
|
||||
let result = match command.kind {
|
||||
CommandKind::Has => {
|
||||
match command.args.len() {
|
||||
// `has <jsonpath>`: Check that `jsonpath` exists.
|
||||
1 => {
|
||||
let val = cache.value();
|
||||
let results = select(val, &command.args[0]).unwrap();
|
||||
!results.is_empty()
|
||||
}
|
||||
// `has <jsonpath> <value>`: Check *any* item matched by `jsonpath` equals `value`.
|
||||
2 => {
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[0]).unwrap();
|
||||
let pat = string_to_value(&command.args[1], cache);
|
||||
let has = results.contains(&pat.as_ref());
|
||||
// Give better error for when `has` check fails.
|
||||
if !command.negated && !has {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"{} matched to {:?} but didn't have {:?}",
|
||||
&command.args[0],
|
||||
results,
|
||||
pat.as_ref()
|
||||
),
|
||||
command,
|
||||
));
|
||||
} else {
|
||||
has
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
/// Performs the actual work of ensuring a command passes.
|
||||
fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
|
||||
let matches = cache.select(&command.path);
|
||||
match &command.kind {
|
||||
CommandKind::HasPath => {
|
||||
if matches.is_empty() {
|
||||
return Err("matched to no values".to_owned());
|
||||
}
|
||||
}
|
||||
CommandKind::HasNotPath => {
|
||||
if !matches.is_empty() {
|
||||
return Err(format!("matched to {matches:?}, but wanted no matches"));
|
||||
}
|
||||
}
|
||||
CommandKind::HasValue { value } => {
|
||||
let want_value = string_to_value(value, cache);
|
||||
if !matches.contains(&want_value.as_ref()) {
|
||||
return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
|
||||
}
|
||||
}
|
||||
CommandKind::Is { value } => {
|
||||
let want_value = string_to_value(value, cache);
|
||||
let matched = get_one(&matches)?;
|
||||
if matched != want_value.as_ref() {
|
||||
return Err(format!("matched to {matched:?} but want {want_value:?}"));
|
||||
}
|
||||
}
|
||||
CommandKind::IsNot { value } => {
|
||||
let wantnt_value = string_to_value(value, cache);
|
||||
let matched = get_one(&matches)?;
|
||||
if matched == wantnt_value.as_ref() {
|
||||
return Err(format!("got value {wantnt_value:?}, but want anything else"));
|
||||
}
|
||||
}
|
||||
// `ismany <path> <jsonpath> <value...>`
|
||||
CommandKind::IsMany => {
|
||||
assert!(!command.negated, "`ismany` may not be negated");
|
||||
let (query, values) = if let [query, values @ ..] = &command.args[..] {
|
||||
(query, values)
|
||||
} else {
|
||||
unreachable!("Checked in CommandKind::validate")
|
||||
};
|
||||
let val = cache.value();
|
||||
let got_values = select(val, &query).unwrap();
|
||||
|
||||
CommandKind::IsMany { values } => {
|
||||
// Serde json doesn't implement Ord or Hash for Value, so we must
|
||||
// use a Vec here. While in theory that makes setwize equality
|
||||
// O(n^2), in practice n will never be large enough to matter.
|
||||
let expected_values =
|
||||
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
|
||||
if expected_values.len() != got_values.len() {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"Expected {} values, but `{}` matched to {} values ({:?})",
|
||||
expected_values.len(),
|
||||
query,
|
||||
got_values.len(),
|
||||
got_values
|
||||
),
|
||||
command,
|
||||
if expected_values.len() != matches.len() {
|
||||
return Err(format!(
|
||||
"Expected {} values, but matched to {} values ({:?})",
|
||||
expected_values.len(),
|
||||
matches.len(),
|
||||
matches
|
||||
));
|
||||
};
|
||||
for got_value in got_values {
|
||||
for got_value in matches {
|
||||
if !expected_values.iter().any(|exp| &**exp == got_value) {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!("`{}` has match {:?}, which was not expected", query, got_value),
|
||||
command,
|
||||
));
|
||||
return Err(format!("has match {got_value:?}, which was not expected",));
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
// `count <jsonpath> <count>`: Check that `jsonpath` matches exactly `count` times.
|
||||
CommandKind::Count => {
|
||||
assert_eq!(command.args.len(), 2);
|
||||
let expected: usize = command.args[1].parse().unwrap();
|
||||
let val = cache.value();
|
||||
let results = select(val, &command.args[0]).unwrap();
|
||||
let eq = results.len() == expected;
|
||||
if !command.negated && !eq {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"`{}` matched to `{:?}` with length {}, but expected length {}",
|
||||
&command.args[0],
|
||||
results,
|
||||
results.len(),
|
||||
expected
|
||||
),
|
||||
command,
|
||||
CommandKind::CountIs { expected } => {
|
||||
if *expected != matches.len() {
|
||||
return Err(format!(
|
||||
"matched to `{matches:?}` with length {}, but expected length {expected}",
|
||||
matches.len(),
|
||||
));
|
||||
} else {
|
||||
eq
|
||||
}
|
||||
}
|
||||
// `has <jsonpath> <value>`: Check` *exactly one* item matched by `jsonpath`, and it equals `value`.
|
||||
CommandKind::Is => {
|
||||
assert_eq!(command.args.len(), 2);
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[0]).unwrap();
|
||||
let pat = string_to_value(&command.args[1], cache);
|
||||
let is = results.len() == 1 && results[0] == pat.as_ref();
|
||||
if !command.negated && !is {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"{} matched to {:?}, but expected {:?}",
|
||||
&command.args[0],
|
||||
results,
|
||||
pat.as_ref()
|
||||
),
|
||||
command,
|
||||
));
|
||||
} else {
|
||||
is
|
||||
}
|
||||
CommandKind::Set { variable } => {
|
||||
let value = get_one(&matches)?;
|
||||
let r = cache.variables.insert(variable.to_owned(), value.clone());
|
||||
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
|
||||
}
|
||||
// `set <name> = <jsonpath>`
|
||||
CommandKind::Set => {
|
||||
assert!(!command.negated, "`set` may not be negated");
|
||||
assert_eq!(command.args.len(), 3);
|
||||
assert_eq!(command.args[1], "=", "Expected an `=`");
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[2]).unwrap();
|
||||
assert_eq!(
|
||||
results.len(),
|
||||
1,
|
||||
"Expected 1 match for `{}` (because of `set`): matched to {:?}",
|
||||
command.args[2],
|
||||
results
|
||||
);
|
||||
match results.len() {
|
||||
0 => false,
|
||||
1 => {
|
||||
let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
|
||||
assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"Got multiple results in `set` for `{}`: {:?}",
|
||||
&command.args[2], results,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if result == command.negated {
|
||||
if command.negated {
|
||||
Err(CkError::FailedCheck(
|
||||
format!("`!{} {}` matched when it shouldn't", command.kind, command.args.join(" ")),
|
||||
command,
|
||||
))
|
||||
} else {
|
||||
// FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
|
||||
Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"`{} {}` didn't match when it should",
|
||||
command.kind,
|
||||
command.args.join(" ")
|
||||
),
|
||||
command,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
|
||||
match matches {
|
||||
[] => Err("matched to no values".to_owned()),
|
||||
[matched] => Ok(matched),
|
||||
_ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use rustc_abi::{ExternAbi, Size};
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_middle::ty::layout::LayoutOf as _;
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
use rustc_span::{BytePos, Loc, Symbol, hygiene};
|
||||
|
|
@ -179,14 +178,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
match flags {
|
||||
0 => {
|
||||
// These are "mutable" allocations as we consider them to be owned by the callee.
|
||||
let name_alloc =
|
||||
this.allocate_str(&name, MiriMemoryKind::Rust.into(), Mutability::Mut)?;
|
||||
let filename_alloc =
|
||||
this.allocate_str(&filename, MiriMemoryKind::Rust.into(), Mutability::Mut)?;
|
||||
|
||||
this.write_immediate(name_alloc.to_ref(this), &this.project_field(dest, 0)?)?;
|
||||
this.write_immediate(filename_alloc.to_ref(this), &this.project_field(dest, 1)?)?;
|
||||
throw_unsup_format!("miri_resolve_frame: v0 is not supported any more");
|
||||
}
|
||||
1 => {
|
||||
this.write_scalar(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
//! metadata we remembered when pushing said frame.
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
|
|
@ -161,7 +160,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let this = self.eval_context_mut();
|
||||
|
||||
// First arg: message.
|
||||
let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into(), Mutability::Not)?;
|
||||
let msg = this.allocate_str_dedup(msg)?;
|
||||
|
||||
// Call the lang item.
|
||||
let panic = this.tcx.lang_items().panic_fn().unwrap();
|
||||
|
|
@ -180,7 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let this = self.eval_context_mut();
|
||||
|
||||
// First arg: message.
|
||||
let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into(), Mutability::Not)?;
|
||||
let msg = this.allocate_str_dedup(msg)?;
|
||||
|
||||
// Call the lang item.
|
||||
let panic = this.tcx.lang_items().panic_nounwind().unwrap();
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
//@normalize-stderr-test: "::<.*>" -> ""
|
||||
|
||||
#[inline(never)]
|
||||
fn func_a() -> Box<[*mut ()]> {
|
||||
func_b::<u8>()
|
||||
}
|
||||
#[inline(never)]
|
||||
fn func_b<T>() -> Box<[*mut ()]> {
|
||||
func_c()
|
||||
}
|
||||
|
||||
macro_rules! invoke_func_d {
|
||||
() => {
|
||||
func_d()
|
||||
};
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn func_c() -> Box<[*mut ()]> {
|
||||
invoke_func_d!()
|
||||
}
|
||||
#[inline(never)]
|
||||
fn func_d() -> Box<[*mut ()]> {
|
||||
unsafe { miri_get_backtrace(0) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut seen_main = false;
|
||||
let frames = func_a();
|
||||
for frame in frames.iter() {
|
||||
let miri_frame = unsafe { miri_resolve_frame(*frame, 0) };
|
||||
let name = String::from_utf8(miri_frame.name.into()).unwrap();
|
||||
let filename = String::from_utf8(miri_frame.filename.into()).unwrap();
|
||||
|
||||
if name == "func_a" {
|
||||
assert_eq!(func_a as *mut (), miri_frame.fn_ptr);
|
||||
}
|
||||
|
||||
// Print every frame to stderr.
|
||||
let out = format!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name);
|
||||
eprintln!("{}", out);
|
||||
// Print the 'main' frame (and everything before it) to stdout, skipping
|
||||
// the printing of internal (and possibly fragile) libstd frames.
|
||||
// Stdout is less normalized so we see more, but it also means we can print less
|
||||
// as platform differences would lead to test suite failures.
|
||||
if !seen_main {
|
||||
println!("{}", out);
|
||||
seen_main = name == "main";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This goes at the bottom of the file so that we can change it
|
||||
// without disturbing line numbers of the functions in the backtrace.
|
||||
|
||||
extern "Rust" {
|
||||
fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>;
|
||||
fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct MiriFrame {
|
||||
name: Box<[u8]>,
|
||||
filename: Box<[u8]>,
|
||||
lineno: u32,
|
||||
colno: u32,
|
||||
fn_ptr: *mut (),
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_d)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_c)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_b)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_a)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (main)
|
||||
RUSTLIB/core/src/ops/function.rs:LL:CC (<fn() as std::ops::FnOnce<()>>::call_once - shim(fn()))
|
||||
RUSTLIB/std/src/sys/backtrace.rs:LL:CC (std::sys::backtrace::__rust_begin_short_backtrace)
|
||||
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start::{closure#0})
|
||||
RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once)
|
||||
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
|
||||
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
|
||||
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
|
||||
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1})
|
||||
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
|
||||
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
|
||||
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
|
||||
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal)
|
||||
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
tests/pass/backtrace/backtrace-api-v0.rs:24:14 (func_d)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:14:9 (func_c)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:9:5 (func_b::<u8>)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:5:5 (func_a)
|
||||
tests/pass/backtrace/backtrace-api-v0.rs:29:18 (main)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# If you want to use this as an .envrc file to create a shell with necessery components
|
||||
# to develop rustc, use the following command in the root of the rusr checkout:
|
||||
#
|
||||
# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && nix flake update --flake ./src/tools/nix-dev-shell && echo .envrc >> .git/info/exclude
|
||||
# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && nix flake update --flake ./src/tools/nix-dev-shell
|
||||
|
||||
if nix flake show path:./src/tools/nix-dev-shell &> /dev/null; then
|
||||
use flake path:./src/tools/nix-dev-shell
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# If you want to use this as an .envrc file to create a shell with necessery components
|
||||
# to develop rustc, use the following command in the root of the rusr checkout:
|
||||
#
|
||||
# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc && echo .envrc >> .git/info/exclude
|
||||
# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc
|
||||
|
||||
use nix ./src/tools/nix-dev-shell/shell.nix
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@
|
|||
# Avoid creating text files for ICEs.
|
||||
RUSTC_ICE = "0";
|
||||
# Provide `libstdc++.so.6` for the self-contained lld.
|
||||
LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
|
||||
stdenv.cc.cc.lib
|
||||
]}";
|
||||
# Provide `libz.so.1`.
|
||||
LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [stdenv.cc.cc.lib zlib]}";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ pkgs.mkShell {
|
|||
# Avoid creating text files for ICEs.
|
||||
RUSTC_ICE = "0";
|
||||
# Provide `libstdc++.so.6` for the self-contained lld.
|
||||
LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
|
||||
stdenv.cc.cc.lib
|
||||
]}";
|
||||
# Provide `libz.so.1`
|
||||
LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [stdenv.cc.cc.lib zlib]}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
bstr = "1.6.0"
|
||||
object = "0.36.2"
|
||||
similar = "2.5.0"
|
||||
wasmparser = { version = "0.216", default-features = false, features = ["std"] }
|
||||
wasmparser = { version = "0.219", default-features = false, features = ["std"] }
|
||||
regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
|
||||
gimli = "0.31.0"
|
||||
build_helper = { path = "../../build_helper" }
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ impl CompletedProcess {
|
|||
/// Checks that `stderr` does not contain the regex pattern `unexpected`.
|
||||
#[track_caller]
|
||||
pub fn assert_stderr_not_contains_regex<S: AsRef<str>>(&self, unexpected: S) -> &Self {
|
||||
assert_not_contains_regex(&self.stdout_utf8(), unexpected);
|
||||
assert_not_contains_regex(&self.stderr_utf8(), unexpected);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,6 +325,12 @@ impl Rustc {
|
|||
self
|
||||
}
|
||||
|
||||
/// Pass the `--verbose` flag.
|
||||
pub fn verbose(&mut self) -> &mut Self {
|
||||
self.cmd.arg("--verbose");
|
||||
self
|
||||
}
|
||||
|
||||
/// `EXTRARSCXXFLAGS`
|
||||
pub fn extra_rs_cxx_flags(&mut self) -> &mut Self {
|
||||
// Adapted from tools.mk (trimmed):
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ dependencies = [
|
|||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -509,6 +515,7 @@ dependencies = [
|
|||
"base-db",
|
||||
"cfg",
|
||||
"either",
|
||||
"expect-test",
|
||||
"hir-def",
|
||||
"hir-expand",
|
||||
"hir-ty",
|
||||
|
|
@ -519,6 +526,9 @@ dependencies = [
|
|||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"test-fixture",
|
||||
"test-utils",
|
||||
"tracing",
|
||||
"triomphe",
|
||||
"tt",
|
||||
|
|
@ -1492,9 +1502,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_abi"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613760a3071b25a67a8d7bc97b37c7fd4722562e9479137b83ae9cf8f8c1601a"
|
||||
checksum = "af462c3a2d524b84a51b6848b439787f01b35c6c1086d3e3086a5f5eea92ed9a"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"ra-ap-rustc_index",
|
||||
|
|
@ -1503,20 +1513,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b2bc6b4ecede8ff28295041e22c2e66853f8e0125990c05135bad3c30bad12c"
|
||||
checksum = "be6bb8cb0ab78d94a222f1ffd3e87254cdfb57413382b8d6ebe26a85482f99d1"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"ra-ap-rustc_index_macros",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index_macros"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2374a39fb2d92d0509178c2b442eadca3cc10e403ef9729a040c1855b08ff261"
|
||||
checksum = "c24b1641455b46e87435b7321219672077066e678963d239a4a2904732979b16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1525,9 +1534,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a2cf8e48b69af3ecc29ed3449892e8a999111d2f75212a78aa242e117cf1711"
|
||||
checksum = "94daa86974417981fed2f12bd8fb00158dfa6fee561152bed689278c846d0272"
|
||||
dependencies = [
|
||||
"unicode-properties",
|
||||
"unicode-xid",
|
||||
|
|
@ -1535,9 +1544,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_parse_format"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d6f59a22b559263c5c42747ae362cf5d4fb272293fa119a4623f8ec288f9656"
|
||||
checksum = "fc07f6bd581746f358e39c4b6bfe8d455b3d6ad1a857821016d0d42eeb5e1e3e"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"ra-ap-rustc_lexer",
|
||||
|
|
@ -1545,9 +1554,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_pattern_analysis"
|
||||
version = "0.80.0"
|
||||
version = "0.85.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7d0575b54ffe09bc5d2f158454bc05f0c30c01d9992310965f854be50ae22b8"
|
||||
checksum = "2f49b86e1276c1c3c72898410def29b699415f4e7d1dfb3531daf79794694372"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"rustc-hash 2.0.0",
|
||||
|
|
@ -1645,6 +1654,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"always-assert",
|
||||
"anyhow",
|
||||
"base64",
|
||||
"cargo_metadata",
|
||||
"cfg",
|
||||
"crossbeam-channel",
|
||||
|
|
@ -1655,6 +1665,7 @@ dependencies = [
|
|||
"hir-def",
|
||||
"hir-ty",
|
||||
"ide",
|
||||
"ide-completion",
|
||||
"ide-db",
|
||||
"ide-ssr",
|
||||
"intern",
|
||||
|
|
@ -1683,6 +1694,7 @@ dependencies = [
|
|||
"stdx",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"tenthash",
|
||||
"test-fixture",
|
||||
"test-utils",
|
||||
"tikv-jemallocator",
|
||||
|
|
@ -1986,6 +1998,12 @@ dependencies = [
|
|||
"tt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tenthash"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb"
|
||||
|
||||
[[package]]
|
||||
name = "test-fixture"
|
||||
version = "0.0.0"
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@ 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.80", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.80", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.80", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.80", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.80", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.85", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.85", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.85", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.85", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.85", default-features = false }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
test-fixture = { path = "./crates/test-fixture" }
|
||||
|
|
|
|||
|
|
@ -547,29 +547,6 @@ impl CrateGraph {
|
|||
None
|
||||
}
|
||||
|
||||
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
|
||||
// As hacky as it gets.
|
||||
pub fn patch_cfg_if(&mut self) -> bool {
|
||||
// we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
|
||||
let cfg_if =
|
||||
self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
|
||||
let std = self.hacky_find_crate("std").next();
|
||||
match (cfg_if, std) {
|
||||
(Some(cfg_if), Some(std)) => {
|
||||
self.arena[cfg_if].dependencies.clear();
|
||||
self.arena[std]
|
||||
.dependencies
|
||||
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
|
||||
self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
|
||||
}
|
||||
|
||||
/// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies.
|
||||
/// Returns a mapping from old crate ids to new crate ids.
|
||||
pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec<Option<CrateId>> {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use crate::{
|
|||
path::{ModPath, Path},
|
||||
src::HasSource,
|
||||
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||
BlockId, DefWithBodyId, HasModule, Lookup,
|
||||
BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
|
||||
};
|
||||
|
||||
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||
|
|
@ -141,7 +141,7 @@ pub struct BodySourceMap {
|
|||
field_map_back: FxHashMap<ExprId, FieldSource>,
|
||||
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
||||
|
||||
types: TypesSourceMap,
|
||||
pub types: TypesSourceMap,
|
||||
|
||||
// FIXME: Make this a sane struct.
|
||||
template_map: Option<
|
||||
|
|
@ -160,9 +160,6 @@ pub struct BodySourceMap {
|
|||
diagnostics: Vec<BodyDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub struct SyntheticSyntax;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum BodyDiagnostic {
|
||||
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
||||
|
|
@ -408,7 +405,8 @@ impl Body {
|
|||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
Expr::Let { expr, pat } => {
|
||||
self.walk_exprs_in_pat(*pat, &mut f);
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. }
|
||||
|
|
@ -442,6 +440,137 @@ impl Body {
|
|||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().for_each(|arm| {
|
||||
f(arm.expr);
|
||||
self.walk_exprs_in_pat(arm.pat, &mut f);
|
||||
});
|
||||
}
|
||||
Expr::Break { expr, .. }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Become { expr } => f(*expr),
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
for field in fields.iter() {
|
||||
f(field.expr);
|
||||
}
|
||||
if let &Some(expr) = spread {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Closure { body, .. } => {
|
||||
f(*body);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, .. } => {
|
||||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let &Some(lhs) = rhs {
|
||||
f(lhs);
|
||||
}
|
||||
if let &Some(rhs) = lhs {
|
||||
f(rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, .. } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
| Expr::Box { expr } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||
Expr::Array(a) => match a {
|
||||
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||
Array::Repeat { initializer, repeat } => {
|
||||
f(*initializer);
|
||||
f(*repeat)
|
||||
}
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.walk_exprs_in_pat(target, &mut f);
|
||||
f(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
|
||||
let expr = &self[expr_id];
|
||||
match expr {
|
||||
Expr::Continue { .. }
|
||||
| Expr::Const(_)
|
||||
| Expr::Missing
|
||||
| Expr::Path(_)
|
||||
| Expr::OffsetOf(_)
|
||||
| Expr::Literal(_)
|
||||
| Expr::Underscore => {}
|
||||
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
f(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
f(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
if let &Some(else_branch) = else_branch {
|
||||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, .. } => {
|
||||
if let &Some(expr) = initializer {
|
||||
f(expr);
|
||||
}
|
||||
if let &Some(expr) = else_branch {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||
Statement::Item(_) => (),
|
||||
}
|
||||
}
|
||||
if let &Some(expr) = tail {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().map(|arm| arm.expr).for_each(f);
|
||||
|
|
@ -498,10 +627,7 @@ impl Body {
|
|||
f(*repeat)
|
||||
}
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.walk_exprs_in_pat(target, &mut f);
|
||||
f(value);
|
||||
}
|
||||
&Expr::Assignment { target: _, value } => f(value),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1510,20 +1510,20 @@ impl ExprCollector<'_> {
|
|||
BuiltinShadowMode::Other,
|
||||
None,
|
||||
);
|
||||
// Funnily enough, record structs/variants *can* be shadowed
|
||||
// by pattern bindings (but unit or tuple structs/variants
|
||||
// can't).
|
||||
match resolved.take_values() {
|
||||
Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
|
||||
Some(ModuleDefId::EnumVariantId(_)) => {
|
||||
// this is only really valid for unit variants, but
|
||||
// shadowing other enum variants with a pattern is
|
||||
// an error anyway
|
||||
Some(ModuleDefId::EnumVariantId(variant))
|
||||
if self.db.variant_data(variant.into()).kind()
|
||||
!= StructKind::Record =>
|
||||
{
|
||||
(None, Pat::Path(name.into()))
|
||||
}
|
||||
Some(ModuleDefId::AdtId(AdtId::StructId(s)))
|
||||
if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
|
||||
{
|
||||
// Funnily enough, record structs *can* be shadowed
|
||||
// by pattern bindings (but unit or tuple structs
|
||||
// can't).
|
||||
(None, Pat::Path(name.into()))
|
||||
}
|
||||
// shadowing statics is an error as well, so we just ignore that case here
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
mod block;
|
||||
|
||||
use base_db::SourceDatabase;
|
||||
use expect_test::{expect, Expect};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
|
|
@ -11,7 +10,7 @@ use super::*;
|
|||
fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut fn_def = None;
|
||||
'outer: for (_, module) in def_map.modules() {
|
||||
|
|
@ -404,3 +403,26 @@ fn foo() {
|
|||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing_record_variant() {
|
||||
let (_, body, _) = lower(
|
||||
r#"
|
||||
enum A {
|
||||
B { field: i32 },
|
||||
}
|
||||
fn f() {
|
||||
use A::*;
|
||||
match () {
|
||||
B => {}
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
|
||||
assert_eq!(
|
||||
body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
|
||||
"B",
|
||||
"should have a binding for `B`",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ impl ImplData {
|
|||
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let impl_def = &item_tree[tree_id.value];
|
||||
let target_trait = impl_def.target_trait.clone();
|
||||
let target_trait = impl_def.target_trait;
|
||||
let self_ty = impl_def.self_ty;
|
||||
let is_negative = impl_def.is_negative;
|
||||
let is_unsafe = impl_def.is_unsafe;
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ use crate::{
|
|||
nameres::{DefMap, MacroSubNs},
|
||||
path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
|
||||
type_ref::{
|
||||
ArrayType, ConstRef, FnType, LifetimeRef, RefType, TypeBound, TypeRef, TypeRefId, TypesMap,
|
||||
TypesSourceMap,
|
||||
ArrayType, ConstRef, FnType, LifetimeRef, PathId, RefType, TypeBound, TypeRef, TypeRefId,
|
||||
TypesMap, TypesSourceMap,
|
||||
},
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||
|
|
@ -224,6 +224,11 @@ impl GenericParams {
|
|||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn no_predicates(&self) -> bool {
|
||||
self.where_predicates.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
|
||||
self.where_predicates.iter()
|
||||
|
|
@ -874,14 +879,20 @@ fn copy_type_bound(
|
|||
to: &mut TypesMap,
|
||||
to_source_map: &mut TypesSourceMap,
|
||||
) -> TypeBound {
|
||||
match bound {
|
||||
TypeBound::Path(path, modifier) => {
|
||||
TypeBound::Path(copy_path(path, from, from_source_map, to, to_source_map), *modifier)
|
||||
let mut copy_path_id = |path: PathId| {
|
||||
let new_path = copy_path(&from[path], from, from_source_map, to, to_source_map);
|
||||
let new_path_id = to.types.alloc(TypeRef::Path(new_path));
|
||||
if let Some(&ptr) = from_source_map.types_map_back.get(path.type_ref()) {
|
||||
to_source_map.types_map_back.insert(new_path_id, ptr);
|
||||
}
|
||||
PathId::from_type_ref_unchecked(new_path_id)
|
||||
};
|
||||
|
||||
match bound {
|
||||
&TypeBound::Path(path, modifier) => TypeBound::Path(copy_path_id(path), modifier),
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
TypeBound::ForLifetime(lifetimes.clone(), copy_path_id(*path))
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => TypeBound::ForLifetime(
|
||||
lifetimes.clone(),
|
||||
copy_path(path, from, from_source_map, to, to_source_map),
|
||||
),
|
||||
TypeBound::Lifetime(lifetime) => TypeBound::Lifetime(lifetime.clone()),
|
||||
TypeBound::Use(use_args) => TypeBound::Use(use_args.clone()),
|
||||
TypeBound::Error => TypeBound::Error,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use crate::{
|
|||
hir::Literal,
|
||||
lower::LowerCtx,
|
||||
path::{GenericArg, Path},
|
||||
SyntheticSyntax,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -91,19 +92,37 @@ impl Rawness {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
/// A `TypeRefId` that is guaranteed to always be `TypeRef::Path`. We use this for things like
|
||||
/// impl's trait, that are always paths but need to be traced back to source code.
|
||||
pub struct PathId(TypeRefId);
|
||||
|
||||
impl PathId {
|
||||
#[inline]
|
||||
pub fn from_type_ref_unchecked(type_ref: TypeRefId) -> Self {
|
||||
Self(type_ref)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_ref(self) -> TypeRefId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TraitRef {
|
||||
pub path: Path,
|
||||
pub path: PathId,
|
||||
}
|
||||
|
||||
impl TraitRef {
|
||||
/// Converts an `ast::PathType` to a `hir::TraitRef`.
|
||||
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> Option<Self> {
|
||||
// FIXME: Use `Path::from_src`
|
||||
match node {
|
||||
ast::Type::PathType(path) => {
|
||||
path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
|
||||
}
|
||||
match &node {
|
||||
ast::Type::PathType(path) => path
|
||||
.path()
|
||||
.and_then(|it| ctx.lower_path(it))
|
||||
.map(|path| TraitRef { path: ctx.alloc_path(path, AstPtr::new(&node)) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -173,11 +192,24 @@ impl TypesMap {
|
|||
impl Index<TypeRefId> for TypesMap {
|
||||
type Output = TypeRef;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: TypeRefId) -> &Self::Output {
|
||||
&self.types[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<PathId> for TypesMap {
|
||||
type Output = Path;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: PathId) -> &Self::Output {
|
||||
let TypeRef::Path(path) = &self[index.type_ref()] else {
|
||||
unreachable!("`PathId` always points to `TypeRef::Path`");
|
||||
};
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypePtr = AstPtr<ast::Type>;
|
||||
pub type TypeSource = InFile<TypePtr>;
|
||||
|
||||
|
|
@ -187,6 +219,12 @@ pub struct TypesSourceMap {
|
|||
}
|
||||
|
||||
impl TypesSourceMap {
|
||||
pub const EMPTY: Self = Self { types_map_back: ArenaMap::new() };
|
||||
|
||||
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
|
||||
self.types_map_back.get(id).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
pub(crate) fn shrink_to_fit(&mut self) {
|
||||
let TypesSourceMap { types_map_back } = self;
|
||||
types_map_back.shrink_to_fit();
|
||||
|
|
@ -214,15 +252,15 @@ impl LifetimeRef {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeBound {
|
||||
Path(Path, TraitBoundModifier),
|
||||
ForLifetime(Box<[Name]>, Path),
|
||||
Path(PathId, TraitBoundModifier),
|
||||
ForLifetime(Box<[Name]>, PathId),
|
||||
Lifetime(LifetimeRef),
|
||||
Use(Box<[UseArgRef]>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const _: [(); 32] = [(); ::std::mem::size_of::<TypeBound>()];
|
||||
const _: [(); 24] = [(); ::std::mem::size_of::<TypeBound>()];
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum UseArgRef {
|
||||
|
|
@ -365,8 +403,8 @@ impl TypeRef {
|
|||
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
|
||||
for bound in bounds {
|
||||
match bound {
|
||||
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
|
||||
go_path(path, f, map)
|
||||
&TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
|
||||
go_path(&map[path], f, map)
|
||||
}
|
||||
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
|
||||
}
|
||||
|
|
@ -397,8 +435,8 @@ impl TypeRef {
|
|||
}
|
||||
for bound in binding.bounds.iter() {
|
||||
match bound {
|
||||
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
|
||||
go_path(path, f, map)
|
||||
&TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
|
||||
go_path(&map[path], f, map)
|
||||
}
|
||||
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
|
||||
}
|
||||
|
|
@ -425,7 +463,7 @@ pub(crate) fn type_bounds_from_ast(
|
|||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::TypeBound) -> Self {
|
||||
let mut lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
|
||||
let mut lower_path_type = |path_type: &ast::PathType| ctx.lower_path(path_type.path()?);
|
||||
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
|
|
@ -433,8 +471,10 @@ impl TypeBound {
|
|||
Some(_) => TraitBoundModifier::Maybe,
|
||||
None => TraitBoundModifier::None,
|
||||
};
|
||||
lower_path_type(path_type)
|
||||
.map(|p| TypeBound::Path(p, m))
|
||||
lower_path_type(&path_type)
|
||||
.map(|p| {
|
||||
TypeBound::Path(ctx.alloc_path(p, AstPtr::new(&path_type).upcast()), m)
|
||||
})
|
||||
.unwrap_or(TypeBound::Error)
|
||||
}
|
||||
ast::TypeBoundKind::ForType(for_type) => {
|
||||
|
|
@ -445,12 +485,14 @@ impl TypeBound {
|
|||
.collect(),
|
||||
None => Box::default(),
|
||||
};
|
||||
let path = for_type.ty().and_then(|ty| match ty {
|
||||
ast::Type::PathType(path_type) => lower_path_type(path_type),
|
||||
let path = for_type.ty().and_then(|ty| match &ty {
|
||||
ast::Type::PathType(path_type) => lower_path_type(path_type).map(|p| (p, ty)),
|
||||
_ => None,
|
||||
});
|
||||
match path {
|
||||
Some(p) => TypeBound::ForLifetime(lt_refs, p),
|
||||
Some((p, ty)) => {
|
||||
TypeBound::ForLifetime(lt_refs, ctx.alloc_path(p, AstPtr::new(&ty)))
|
||||
}
|
||||
None => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
|
|
@ -470,10 +512,10 @@ impl TypeBound {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
|
||||
pub fn as_path<'a>(&self, map: &'a TypesMap) -> Option<(&'a Path, TraitBoundModifier)> {
|
||||
match self {
|
||||
TypeBound::Path(p, m) => Some((p, m)),
|
||||
TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
|
||||
&TypeBound::Path(p, m) => Some((&map[p], m)),
|
||||
&TypeBound::ForLifetime(_, p) => Some((&map[p], TraitBoundModifier::None)),
|
||||
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::{
|
|||
lower::LowerCtx,
|
||||
path::AssociatedTypeBinding,
|
||||
type_ref::{
|
||||
LifetimeRef, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
|
||||
LifetimeRef, PathId, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
|
||||
TypesMap, TypesSourceMap,
|
||||
},
|
||||
visibility::RawVisibility,
|
||||
|
|
@ -514,7 +514,7 @@ impl<'a> Ctx<'a> {
|
|||
};
|
||||
|
||||
let ret_type = if func.async_token().is_some() {
|
||||
let future_impl = desugar_future_path(ret_type);
|
||||
let future_impl = desugar_future_path(&mut body_ctx, ret_type);
|
||||
let ty_bound = TypeBound::Path(future_impl, TraitBoundModifier::None);
|
||||
body_ctx.alloc_type_ref_desugared(TypeRef::ImplTrait(ThinVec::from_iter([ty_bound])))
|
||||
} else {
|
||||
|
|
@ -936,7 +936,7 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(orig: TypeRefId) -> Path {
|
||||
fn desugar_future_path(ctx: &mut LowerCtx<'_>, orig: TypeRefId) -> PathId {
|
||||
let path = path![core::future::Future];
|
||||
let mut generic_args: Vec<_> =
|
||||
std::iter::repeat(None).take(path.segments().len() - 1).collect();
|
||||
|
|
@ -948,7 +948,8 @@ fn desugar_future_path(orig: TypeRefId) -> Path {
|
|||
};
|
||||
generic_args.push(Some(GenericArgs { bindings: Box::new([binding]), ..GenericArgs::empty() }));
|
||||
|
||||
Path::from_known_path(path, generic_args)
|
||||
let path = Path::from_known_path(path, generic_args);
|
||||
PathId::from_type_ref_unchecked(ctx.alloc_type_ref_desugared(TypeRef::Path(path)))
|
||||
}
|
||||
|
||||
enum HasImplicitSelf {
|
||||
|
|
|
|||
|
|
@ -484,7 +484,7 @@ impl Printer<'_> {
|
|||
w!(self, "!");
|
||||
}
|
||||
if let Some(tr) = target_trait {
|
||||
self.print_path(&tr.path, types_map);
|
||||
self.print_path(&types_map[tr.path], types_map);
|
||||
w!(self, " for ");
|
||||
}
|
||||
self.print_type_ref(*self_ty, types_map);
|
||||
|
|
@ -648,9 +648,9 @@ impl Printer<'_> {
|
|||
let (target, bound) = match pred {
|
||||
WherePredicate::TypeBound { target, bound } => (target, bound),
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
wln!(
|
||||
w!(
|
||||
this,
|
||||
"{}: {},",
|
||||
"{}: {}",
|
||||
target.name.display(self.db.upcast(), edition),
|
||||
bound.name.display(self.db.upcast(), edition)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -351,7 +351,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
|||
where
|
||||
T: Copy,
|
||||
T: 'a,
|
||||
T: 'b
|
||||
T: 'b,
|
||||
'b: 'a
|
||||
{
|
||||
pub(self) field: &'a &'b T,
|
||||
}
|
||||
|
|
@ -370,7 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
|||
where
|
||||
T: Copy,
|
||||
T: 'a,
|
||||
T: 'b
|
||||
T: 'b,
|
||||
'b: 'a
|
||||
{
|
||||
// AstId: 9
|
||||
pub(self) fn f<G>(
|
||||
|
|
|
|||
|
|
@ -376,6 +376,9 @@ language_item_table! {
|
|||
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
|
|
|
|||
|
|
@ -1535,3 +1535,6 @@ fn macro_call_as_call_id_with_eager(
|
|||
pub struct UnresolvedMacro {
|
||||
pub path: hir_expand::mod_path::ModPath,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub struct SyntheticSyntax;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
use std::{cell::OnceCell, mem};
|
||||
|
||||
use hir_expand::{span_map::SpanMap, AstId, HirFileId, InFile};
|
||||
use span::{AstIdMap, AstIdNode};
|
||||
use span::{AstIdMap, AstIdNode, Edition, EditionedFileId, FileId, RealSpanMap};
|
||||
use stdx::thin_vec::ThinVec;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
|
@ -10,7 +10,7 @@ use triomphe::Arc;
|
|||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::Path,
|
||||
type_ref::{TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||
type_ref::{PathId, TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||
};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
|
|
@ -63,6 +63,30 @@ impl<'a> LowerCtx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Prepares a `LowerCtx` for synthetic AST that needs to be lowered. This is intended for IDE things.
|
||||
pub fn for_synthetic_ast(
|
||||
db: &'a dyn DefDatabase,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
) -> Self {
|
||||
let file_id = EditionedFileId::new(
|
||||
FileId::from_raw(EditionedFileId::MAX_FILE_ID),
|
||||
Edition::Edition2015,
|
||||
);
|
||||
LowerCtx {
|
||||
db,
|
||||
// Make up an invalid file id, so that if we will try to actually access it salsa will panic.
|
||||
file_id: file_id.into(),
|
||||
span_map: SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(file_id))).into(),
|
||||
ast_id_map: ast_id_map.into(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
outer_impl_trait: false,
|
||||
types_map,
|
||||
types_source_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn span_map(&self) -> &SpanMap {
|
||||
self.span_map.get_or_init(|| self.db.span_map(self.file_id))
|
||||
}
|
||||
|
|
@ -118,4 +142,8 @@ impl<'a> LowerCtx<'a> {
|
|||
pub(crate) fn alloc_error_type(&mut self) -> TypeRefId {
|
||||
self.types_map.types.alloc(TypeRef::Error)
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_path(&mut self, path: Path, node: TypePtr) -> PathId {
|
||||
PathId::from_type_ref_unchecked(self.alloc_type_ref(TypeRef::Path(path), node))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1733,7 +1733,7 @@ m!(C("0"));
|
|||
macro_rules! m {
|
||||
($k:expr) => { fn f() { K::$k; } }
|
||||
}
|
||||
/* parse error: expected identifier */
|
||||
/* parse error: expected identifier, `self`, `super`, `crate`, or `Self` */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
|
|
@ -1759,8 +1759,9 @@ fn f() {
|
|||
// NAME_REF@6..7
|
||||
// IDENT@6..7 "K"
|
||||
// COLON2@7..9 "::"
|
||||
// ERROR@9..10
|
||||
// L_PAREN@9..10 "("
|
||||
// PATH_SEGMENT@9..10
|
||||
// ERROR@9..10
|
||||
// L_PAREN@9..10 "("
|
||||
// EXPR_STMT@10..16
|
||||
// CALL_EXPR@10..16
|
||||
// PATH_EXPR@10..11
|
||||
|
|
|
|||
|
|
@ -184,3 +184,31 @@ fn test() {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_variable_raw_name_equals_non_raw() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
($r#name:tt) => {
|
||||
$name
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
m!(1234)
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
($r#name:tt) => {
|
||||
$name
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
1234
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ mod proc_macros;
|
|||
|
||||
use std::{iter, ops::Range, sync};
|
||||
|
||||
use base_db::SourceDatabase;
|
||||
use expect_test::Expect;
|
||||
use hir_expand::{
|
||||
db::ExpandDatabase,
|
||||
|
|
@ -63,7 +62,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
},
|
||||
)];
|
||||
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let local_id = DefMap::ROOT;
|
||||
let module = def_map.module_id(local_id);
|
||||
|
|
|
|||
|
|
@ -910,8 +910,13 @@ impl DefCollector<'_> {
|
|||
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
|
||||
// record the glob import in case we add further items
|
||||
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||
if !glob.iter().any(|(mid, _, _)| *mid == module_id) {
|
||||
glob.push((module_id, vis, id));
|
||||
match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) {
|
||||
None => glob.push((module_id, vis, id)),
|
||||
Some((_, old_vis, _)) => {
|
||||
if let Some(new_vis) = old_vis.max(vis, &self.def_map) {
|
||||
*old_vis = new_vis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB};
|
|||
|
||||
fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = db.fetch_test_crate();
|
||||
db.crate_def_map(krate)
|
||||
}
|
||||
|
||||
fn render_crate_def_map(ra_fixture: &str) -> String {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = db.fetch_test_crate();
|
||||
db.crate_def_map(krate).dump(&db)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -451,3 +451,42 @@ mod glob_target {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_18580() {
|
||||
check(
|
||||
r#"
|
||||
pub mod libs {
|
||||
pub struct Placeholder;
|
||||
}
|
||||
|
||||
pub mod reexport_2 {
|
||||
use reexport_1::*;
|
||||
pub use reexport_1::*;
|
||||
|
||||
pub mod reexport_1 {
|
||||
pub use crate::libs::*;
|
||||
}
|
||||
}
|
||||
|
||||
use reexport_2::*;
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Placeholder: t v
|
||||
libs: t
|
||||
reexport_1: t
|
||||
reexport_2: t
|
||||
|
||||
crate::libs
|
||||
Placeholder: t v
|
||||
|
||||
crate::reexport_2
|
||||
Placeholder: t v
|
||||
reexport_1: t
|
||||
|
||||
crate::reexport_2::reexport_1
|
||||
Placeholder: t v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
use base_db::{SourceDatabase, SourceDatabaseFileInputExt as _};
|
||||
use base_db::SourceDatabaseFileInputExt as _;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
|
||||
|
||||
fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) {
|
||||
let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
|
||||
let krate = {
|
||||
let crate_graph = db.crate_graph();
|
||||
// Some of these tests use minicore/proc-macros which will be injected as the first crate
|
||||
crate_graph.iter().last().unwrap()
|
||||
};
|
||||
let krate = db.fetch_test_crate();
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
db.crate_def_map(krate);
|
||||
|
|
@ -120,28 +116,31 @@ fn f() { foo }
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_an_attribute_arg_should_not_invalidate_def_map() {
|
||||
check_def_map_is_not_recomputed(
|
||||
r"
|
||||
//- proc_macros: identity
|
||||
//- /lib.rs
|
||||
mod foo;
|
||||
// Would be nice if this was the case, but as attribute inputs are stored in the item tree, this is
|
||||
// not currently the case.
|
||||
// #[test]
|
||||
// fn typing_inside_an_attribute_arg_should_not_invalidate_def_map() {
|
||||
// check_def_map_is_not_recomputed(
|
||||
// r"
|
||||
// //- proc_macros: identity
|
||||
// //- /lib.rs
|
||||
// mod foo;
|
||||
|
||||
//- /foo/mod.rs
|
||||
pub mod bar;
|
||||
// //- /foo/mod.rs
|
||||
// pub mod bar;
|
||||
|
||||
// //- /foo/bar.rs
|
||||
// $0
|
||||
// #[proc_macros::identity]
|
||||
// fn f() {}
|
||||
// ",
|
||||
// r"
|
||||
// #[proc_macros::identity(foo)]
|
||||
// fn f() {}
|
||||
// ",
|
||||
// );
|
||||
// }
|
||||
|
||||
//- /foo/bar.rs
|
||||
$0
|
||||
#[proc_macros::identity]
|
||||
fn f() {}
|
||||
",
|
||||
r"
|
||||
#[proc_macros::identity(foo)]
|
||||
fn f() {}
|
||||
",
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn typing_inside_macro_heavy_file_should_not_invalidate_def_map() {
|
||||
check_def_map_is_not_recomputed(
|
||||
|
|
@ -198,31 +197,33 @@ pub struct S {}
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_derive_should_not_invalidate_def_map() {
|
||||
check_def_map_is_not_recomputed(
|
||||
r"
|
||||
//- proc_macros: derive_identity
|
||||
//- minicore:derive
|
||||
//- /lib.rs
|
||||
mod foo;
|
||||
// Would be nice if this was the case, but as attribute inputs are stored in the item tree, this is
|
||||
// not currently the case.
|
||||
// #[test]
|
||||
// fn typing_inside_a_derive_should_not_invalidate_def_map() {
|
||||
// check_def_map_is_not_recomputed(
|
||||
// r"
|
||||
// //- proc_macros: derive_identity
|
||||
// //- minicore:derive
|
||||
// //- /lib.rs
|
||||
// mod foo;
|
||||
|
||||
//- /foo/mod.rs
|
||||
pub mod bar;
|
||||
// //- /foo/mod.rs
|
||||
// pub mod bar;
|
||||
|
||||
//- /foo/bar.rs
|
||||
$0
|
||||
#[derive(proc_macros::DeriveIdentity)]
|
||||
#[allow()]
|
||||
struct S;
|
||||
",
|
||||
r"
|
||||
#[derive(proc_macros::DeriveIdentity)]
|
||||
#[allow(dead_code)]
|
||||
struct S;
|
||||
",
|
||||
);
|
||||
}
|
||||
// //- /foo/bar.rs
|
||||
// $0
|
||||
// #[derive(proc_macros::DeriveIdentity)]
|
||||
// #[allow()]
|
||||
// struct S;
|
||||
// ",
|
||||
// r"
|
||||
// #[derive(proc_macros::DeriveIdentity)]
|
||||
// #[allow(dead_code)]
|
||||
// struct S;
|
||||
// ",
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_function_should_not_invalidate_item_expansions() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
|
||||
mod lower;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
|
|
@ -19,6 +21,8 @@ use syntax::ast;
|
|||
|
||||
pub use hir_expand::mod_path::{path, ModPath, PathKind};
|
||||
|
||||
pub use lower::hir_segment_to_ast_segment;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ImportAlias {
|
||||
/// Unnamed alias, as in `use Foo as _;`
|
||||
|
|
@ -230,7 +234,7 @@ impl Path {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PathSegment<'a> {
|
||||
pub name: &'a Name,
|
||||
pub args_and_bindings: Option<&'a GenericArgs>,
|
||||
|
|
@ -274,6 +278,12 @@ impl<'a> PathSegments<'a> {
|
|||
generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)),
|
||||
}
|
||||
}
|
||||
pub fn strip_last(&self) -> PathSegments<'a> {
|
||||
PathSegments {
|
||||
segments: self.segments.split_last().map_or(&[], |it| it.1),
|
||||
generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)),
|
||||
}
|
||||
}
|
||||
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
|
||||
self.segments
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -17,13 +17,31 @@ use crate::{
|
|||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
|
||||
pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
|
||||
}
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// It correctly handles `$crate` based path from macro call.
|
||||
// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
|
||||
// also needs an update.
|
||||
pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut type_anchor = None;
|
||||
let mut segments = Vec::new();
|
||||
let mut generic_args = Vec::new();
|
||||
#[cfg(test)]
|
||||
let mut ast_segments = Vec::new();
|
||||
#[cfg(test)]
|
||||
let mut ast_segments_offset = 0;
|
||||
#[allow(unused_mut)]
|
||||
let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
|
||||
#[cfg(test)]
|
||||
ast_segments.push(_segment.clone());
|
||||
segments.push(name);
|
||||
};
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
|
|
@ -34,6 +52,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
if name_ref.text() == "$crate" {
|
||||
if path.qualifier().is_some() {
|
||||
// FIXME: Report an error.
|
||||
return None;
|
||||
}
|
||||
break kind = resolve_crate_root(
|
||||
ctx.db.upcast(),
|
||||
ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx,
|
||||
|
|
@ -48,7 +70,7 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
.or_else(|| {
|
||||
lower_generic_args_from_fn_path(
|
||||
ctx,
|
||||
segment.param_list(),
|
||||
segment.parenthesized_arg_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
});
|
||||
|
|
@ -56,10 +78,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
generic_args.resize(segments.len(), None);
|
||||
generic_args.push(args);
|
||||
}
|
||||
segments.push(name);
|
||||
push_segment(&segment, &mut segments, name);
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => {
|
||||
segments.push(Name::new_symbol_root(sym::Self_.clone()));
|
||||
push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_.clone()));
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
|
@ -81,6 +103,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
kind = mod_path.kind;
|
||||
|
||||
segments.extend(mod_path.segments().iter().cloned().rev());
|
||||
#[cfg(test)]
|
||||
{
|
||||
ast_segments_offset = mod_path.segments().len();
|
||||
}
|
||||
if let Some(path_generic_args) = path_generic_args {
|
||||
generic_args.resize(segments.len() - num_segments, None);
|
||||
generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
|
||||
|
|
@ -112,10 +138,18 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if path.qualifier().is_some() {
|
||||
// FIXME: Report an error.
|
||||
return None;
|
||||
}
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
if path.qualifier().is_some() {
|
||||
// FIXME: Report an error.
|
||||
return None;
|
||||
}
|
||||
// don't break out if `self` is the last segment of a path, this mean we got a
|
||||
// use tree like `foo::{self}` which we want to resolve as `foo`
|
||||
if !segments.is_empty() {
|
||||
|
|
@ -162,6 +196,13 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
ast_segments.reverse();
|
||||
SEGMENT_LOWERING_MAP
|
||||
.with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
|
||||
}
|
||||
|
||||
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
|
||||
if type_anchor.is_none() && generic_args.is_empty() {
|
||||
return Some(Path::BarePath(mod_path));
|
||||
|
|
@ -181,6 +222,41 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
}
|
||||
}
|
||||
|
||||
/// This function finds the AST segment that corresponds to the HIR segment
|
||||
/// with index `segment_idx` on the path that is lowered from `path`.
|
||||
pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
|
||||
// Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
|
||||
// as keeping source maps for all paths segments will have a severe impact on memory usage.
|
||||
|
||||
let mut segments = path.segments();
|
||||
if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
|
||||
segments.clone().next().and_then(|it| it.kind())
|
||||
{
|
||||
segments.next();
|
||||
return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
|
||||
}
|
||||
return find_segment(segments, segment_idx);
|
||||
|
||||
fn find_segment(
|
||||
segments: impl Iterator<Item = ast::PathSegment>,
|
||||
segment_idx: u32,
|
||||
) -> Option<ast::PathSegment> {
|
||||
segments
|
||||
.filter(|segment| match segment.kind() {
|
||||
Some(
|
||||
ast::PathSegmentKind::CrateKw
|
||||
| ast::PathSegmentKind::SelfKw
|
||||
| ast::PathSegmentKind::SuperKw
|
||||
| ast::PathSegmentKind::Type { .. },
|
||||
)
|
||||
| None => false,
|
||||
Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
|
||||
Some(ast::PathSegmentKind::SelfTypeKw) => true,
|
||||
})
|
||||
.nth(segment_idx as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_generic_args(
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
node: ast::GenericArgList,
|
||||
|
|
@ -247,12 +323,12 @@ pub(super) fn lower_generic_args(
|
|||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
fn lower_generic_args_from_fn_path(
|
||||
ctx: &mut LowerCtx<'_>,
|
||||
params: Option<ast::ParamList>,
|
||||
args: Option<ast::ParenthesizedArgList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let params = params?;
|
||||
let params = args?;
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
for param in params.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
|
|
|
|||
126
src/tools/rust-analyzer/crates/hir-def/src/path/tests.rs
Normal file
126
src/tools/rust-analyzer/crates/hir-def/src/path/tests.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use expect_test::{expect, Expect};
|
||||
use span::Edition;
|
||||
use syntax::ast::{self, make};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{
|
||||
lower::LowerCtx,
|
||||
path::{
|
||||
lower::{hir_segment_to_ast_segment, SEGMENT_LOWERING_MAP},
|
||||
Path,
|
||||
},
|
||||
pretty,
|
||||
test_db::TestDB,
|
||||
type_ref::{TypesMap, TypesSourceMap},
|
||||
};
|
||||
|
||||
fn lower_path(path: ast::Path) -> (TestDB, TypesMap, Option<Path>) {
|
||||
let (db, file_id) = TestDB::with_single_file("");
|
||||
let mut types_map = TypesMap::default();
|
||||
let mut types_source_map = TypesSourceMap::default();
|
||||
let mut ctx = LowerCtx::new(&db, file_id.into(), &mut types_map, &mut types_source_map);
|
||||
let lowered_path = ctx.lower_path(path);
|
||||
(db, types_map, lowered_path)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_hir_to_ast(path: &str, ignore_segments: &[&str]) {
|
||||
let path = make::path_from_text(path);
|
||||
SEGMENT_LOWERING_MAP.with_borrow_mut(|map| map.clear());
|
||||
let _ = lower_path(path.clone()).2.expect("failed to lower path");
|
||||
SEGMENT_LOWERING_MAP.with_borrow(|map| {
|
||||
for (segment, segment_idx) in map {
|
||||
if ignore_segments.contains(&&*segment.to_string()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let restored_segment = hir_segment_to_ast_segment(&path, *segment_idx as u32)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"failed to map back segment `{segment}` \
|
||||
numbered {segment_idx} in HIR from path `{path}`"
|
||||
)
|
||||
});
|
||||
assert_eq!(
|
||||
segment, &restored_segment,
|
||||
"mapping back `{segment}` numbered {segment_idx} in HIR \
|
||||
from path `{path}` produced incorrect segment `{restored_segment}`"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_trait_ref() {
|
||||
check_hir_to_ast("<A as B::C::D>::E::F", &["A"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_plain_path() {
|
||||
check_hir_to_ast("A::B::C::D::E::F", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_crate_path() {
|
||||
check_hir_to_ast("crate::A::B::C", &[]);
|
||||
check_hir_to_ast("crate::super::super::A::B::C", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_self_path() {
|
||||
check_hir_to_ast("self::A::B::C", &[]);
|
||||
check_hir_to_ast("self::super::super::A::B::C", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_super_path() {
|
||||
check_hir_to_ast("super::A::B::C", &[]);
|
||||
check_hir_to_ast("super::super::super::A::B::C", &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_type_anchor_path() {
|
||||
check_hir_to_ast("<A::B>::C::D", &["A", "B"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hir_to_ast_path_super_in_middle() {
|
||||
check_hir_to_ast("A::super::B::super::super::C::D", &[]);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_fail_lowering(path: &str) {
|
||||
let (_, _, lowered_path) = lower_path(make::path_from_text(path));
|
||||
assert!(lowered_path.is_none(), "path `{path}` should fail lowering");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keywords_in_middle_fail_lowering1() {
|
||||
check_fail_lowering("self::A::self::B::super::C::crate::D");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keywords_in_middle_fail_lowering2() {
|
||||
check_fail_lowering("A::super::self::C::D");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keywords_in_middle_fail_lowering3() {
|
||||
check_fail_lowering("A::crate::B::C::D");
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_path_lowering(path: &str, expected: Expect) {
|
||||
let (db, types_map, lowered_path) = lower_path(make::path_from_text(path));
|
||||
let lowered_path = lowered_path.expect("failed to lower path");
|
||||
let mut buf = String::new();
|
||||
pretty::print_path(&db, &lowered_path, &types_map, &mut buf, Edition::CURRENT)
|
||||
.expect("failed to pretty-print path");
|
||||
expected.assert_eq(&buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_like_path_with_coloncolon() {
|
||||
check_path_lowering("Fn::(A, B) -> C", expect![[r#"Fn::<(A, B), Output = C>"#]]);
|
||||
check_path_lowering("Fn::(A, B)", expect![[r#"Fn::<(A, B), Output = ()>"#]]);
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ pub(crate) fn print_type_bounds(
|
|||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => write!(buf, "?")?,
|
||||
}
|
||||
print_path(db, path, map, buf, edition)?;
|
||||
print_path(db, &map[*path], map, buf, edition)?;
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
write!(
|
||||
|
|
@ -279,7 +279,7 @@ pub(crate) fn print_type_bounds(
|
|||
"for<{}> ",
|
||||
lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
|
||||
)?;
|
||||
print_path(db, path, map, buf, edition)?;
|
||||
print_path(db, &map[*path], map, buf, edition)?;
|
||||
}
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
|
||||
TypeBound::Use(args) => {
|
||||
|
|
|
|||
|
|
@ -576,10 +576,12 @@ impl Resolver {
|
|||
match scope {
|
||||
Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()),
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
|
||||
if let Some(TypeNs::TraitId(trait_)) =
|
||||
self.resolve_path_in_type_ns_fully(db, &target_trait.path)
|
||||
{
|
||||
let impl_data = db.impl_data(impl_);
|
||||
if let Some(target_trait) = impl_data.target_trait {
|
||||
if let Some(TypeNs::TraitId(trait_)) = self.resolve_path_in_type_ns_fully(
|
||||
db,
|
||||
&impl_data.types_map[target_trait.path],
|
||||
) {
|
||||
traits.insert(trait_);
|
||||
}
|
||||
}
|
||||
|
|
@ -640,9 +642,9 @@ impl Resolver {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn generic_params(&self) -> Option<&Arc<GenericParams>> {
|
||||
pub fn generic_params(&self) -> Option<&GenericParams> {
|
||||
self.scopes().find_map(|scope| match scope {
|
||||
Scope::GenericParams { params, .. } => Some(params),
|
||||
Scope::GenericParams { params, .. } => Some(&**params),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,19 @@ impl FileLoader for TestDB {
|
|||
}
|
||||
|
||||
impl TestDB {
|
||||
pub(crate) fn fetch_test_crate(&self) -> CrateId {
|
||||
let crate_graph = self.crate_graph();
|
||||
let it = crate_graph
|
||||
.iter()
|
||||
.find(|&idx| {
|
||||
crate_graph[idx].display_name.as_ref().map(|it| it.canonical_name().as_str())
|
||||
== Some("ra_test_fixture")
|
||||
})
|
||||
.or_else(|| crate_graph.iter().next())
|
||||
.unwrap();
|
||||
it
|
||||
}
|
||||
|
||||
pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
|
||||
for &krate in self.relevant_crates(file_id).iter() {
|
||||
let crate_def_map = self.crate_def_map(krate);
|
||||
|
|
|
|||
|
|
@ -180,6 +180,13 @@ impl<FileId: FileIdToSyntax, T> InFileWrapper<FileId, T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(private_bounds)]
|
||||
impl<FileId: FileIdToSyntax, N: AstNode> InFileWrapper<FileId, AstPtr<N>> {
|
||||
pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
|
||||
self.value.to_node(&self.file_syntax(db))
|
||||
}
|
||||
}
|
||||
|
||||
impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
|
||||
pub fn syntax(&self) -> InFileWrapper<FileId, &SyntaxNode> {
|
||||
self.with_value(self.value.syntax())
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ pub(crate) fn fixup_syntax(
|
|||
}
|
||||
},
|
||||
ast::ExprStmt(it) => {
|
||||
if it.semicolon_token().is_none() {
|
||||
let needs_semi = it.semicolon_token().is_none() && it.expr().map_or(false, |e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR);
|
||||
if needs_semi {
|
||||
append.insert(node.clone().into(), vec![
|
||||
Leaf::Punct(Punct {
|
||||
char: ';',
|
||||
|
|
@ -905,6 +906,21 @@ fn foo() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {|| __ra_fixup}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixup_regression_() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
{}
|
||||
{}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {{} {}}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,13 @@ pub enum MacroDefKind {
|
|||
ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
|
||||
}
|
||||
|
||||
impl MacroDefKind {
|
||||
#[inline]
|
||||
pub fn is_declarative(&self) -> bool {
|
||||
matches!(self, MacroDefKind::Declarative(..))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EagerCallInfo {
|
||||
/// The expanded argument of the eager macro.
|
||||
|
|
|
|||
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