diff options
| author | Ralf Jung <post@ralfj.de> | 2025-01-08 09:23:40 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2025-01-08 09:23:40 +0100 |
| commit | 2d180714e14b34e36bf883bdf706ebaf5fa96754 (patch) | |
| tree | 36b4829675d832d323283fc37457f6509f8a519c /src | |
| parent | fc4a52f598f6259a98964aa3290e9e74e396c1e3 (diff) | |
| parent | 67f49010adf4ec3238564ec072b3652179813dd1 (diff) | |
| download | rust-2d180714e14b34e36bf883bdf706ebaf5fa96754.tar.gz rust-2d180714e14b34e36bf883bdf706ebaf5fa96754.zip | |
Merge from rustc
Diffstat (limited to 'src')
544 files changed, 38476 insertions, 7744 deletions
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 7fd811ac507..d8d862caf6a 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,5 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use std::path::PathBuf; - use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -10,7 +8,8 @@ use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, }; use crate::core::config::TargetSelection; -use crate::{Compiler, Mode, Subcommand}; +use crate::utils::build_stamp::{self, BuildStamp}; +use crate::{Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { @@ -83,22 +82,16 @@ impl Step for Std { format_args!("library artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check"); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); // We skip populating the sysroot in non-zero stage because that'll lead // to rlib/rmeta conflicts if std gets built during this session. if compiler.stage == 0 { let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } drop(_guard); @@ -139,16 +132,9 @@ impl Step for Std { cargo.arg("-p").arg(krate); } + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test"); let _guard = builder.msg_check("library test/bench/example targets", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - false, - ); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -249,19 +235,14 @@ impl Step for Rustc { format_args!("compiler artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &librustc_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } } @@ -315,15 +296,10 @@ impl Step for CodegenBackend { let _guard = builder.msg_check(backend, target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &codegen_backend_stamp(builder, compiler, target, backend), - vec![], - true, - false, - ); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -344,7 +320,7 @@ impl Step for RustAnalyzer { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")), + .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")), ) } @@ -380,22 +356,13 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - let _guard = builder.msg_check("rust-analyzer artifacts", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &stamp(builder, compiler, target), - vec![], - true, - false, - ); + // Cargo's output path in a given stage, compiled by a particular + // compiler for the specified target. + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix("rust-analyzer-check"); - /// Cargo's output path in a given stage, compiled by a particular - /// compiler for the specified target. - fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp") - } + let _guard = builder.msg_check("rust-analyzer artifacts", target); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -469,9 +436,8 @@ fn run_tool_check_step( cargo.arg("--all-targets"); } - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", step_type_name.to_lowercase())); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", step_type_name.to_lowercase())); let _guard = builder.msg_check(format!("{display_name} artifacts"), target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); @@ -499,38 +465,3 @@ tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: f // Compiletest is implicitly "checked" when it gets built in order to run tests, // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false }); - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") -} - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_test_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}-check.stamp")) -} diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 61cc9eeed55..21e9fea9363 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -10,6 +10,7 @@ use std::io::{self, ErrorKind}; use std::path::Path; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, crate_description}; +use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; use crate::{Build, Compiler, Kind, Mode, Subcommand}; @@ -146,7 +147,7 @@ fn clean_default(build: &Build) { rm_rf(&build.out.join("dist")); rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id")); rm_rf(&build.out.join("bootstrap-shims-dump")); - rm_rf(&build.out.join("rustfmt.stamp")); + rm_rf(BuildStamp::new(&build.out).with_prefix("rustfmt").path()); let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect(); // After cross-compilation, artifacts of the host architecture (which may differ from build.host) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 518db156fea..fe7e4a77f71 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -1,12 +1,13 @@ //! Implementation of running clippy on the compiler, standard library and various tools. -use super::compile::{librustc_stamp, libstd_stamp, run_cargo, rustc_cargo, std_cargo}; +use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use super::{check, compile}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::compile::std_crates_for_run_make; use crate::core::builder; use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::{Mode, Subcommand, TargetSelection}; /// Disable the most spammy clippy lints @@ -167,7 +168,7 @@ impl Step for Std { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), vec![], true, false, @@ -243,7 +244,7 @@ impl Step for Rustc { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), vec![], true, false, @@ -307,9 +308,9 @@ macro_rules! lint_any { &target, ); - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())); + let stringified_name = stringify!($name).to_lowercase(); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", stringified_name)); run_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 148b96181d1..4c4df922b37 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -24,6 +24,8 @@ use crate::core::builder::{ Builder, Cargo, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath, crate_description, }; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; +use crate::utils::build_stamp; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, @@ -254,7 +256,7 @@ impl Step for Std { builder, cargo, vec![], - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -469,7 +471,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins { + let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. // But, the user could still decide to manually use an in-tree submodule. // @@ -644,7 +646,12 @@ impl Step for StdLink { (libdir, hostdir) }; - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot( + builder, + &libdir, + &hostdir, + &build_stamp::libstd_stamp(builder, compiler, target), + ); // Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0` // work for stage0-sysroot. We only do this if the stage0 compiler comes from beta, @@ -973,7 +980,7 @@ impl Step for Rustc { compiler.host, target, ); - let stamp = librustc_stamp(builder, compiler, target); + let stamp = build_stamp::librustc_stamp(builder, compiler, target); run_cargo( builder, cargo, @@ -984,7 +991,7 @@ impl Step for Rustc { true, // Only ship rustc_driver.so and .rmeta files, not all intermediate .rlib files. ); - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // When building `librustc_driver.so` (like `libLLVM.so`) on linux, it can contain // unexpected debuginfo from dependencies, for example from the C++ standard library used in // our LLVM wrapper. Unless we're explicitly requesting `librustc_driver` to be built with @@ -1207,6 +1214,15 @@ pub fn rustc_cargo_env( rustc_llvm_env(builder, cargo, target) } } + + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if builder.config.jemalloc + && target.starts_with("aarch64") + && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() + { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } } /// Pass down configuration from the LLVM build into the build of @@ -1320,7 +1336,7 @@ impl Step for RustcLink { builder, &builder.sysroot_target_libdir(previous_stage_compiler, target), &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), ); } } @@ -1438,7 +1454,7 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); rustc_cargo_env(builder, &mut cargo, target, compiler.stage); - let tmp_stamp = out_dir.join(".tmp.stamp"); + let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); let _guard = builder.msg_build(compiler, format_args!("codegen backend {backend}"), target); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); @@ -1460,9 +1476,9 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = codegen_backend_stamp(builder, compiler, target, &backend); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend); let codegen_backend = codegen_backend.to_str().unwrap(); - t!(fs::write(stamp, codegen_backend)); + t!(stamp.add_stamp(codegen_backend).write()); } } @@ -1499,8 +1515,8 @@ fn copy_codegen_backends_to_sysroot( continue; // Already built as part of rustc } - let stamp = codegen_backend_stamp(builder, compiler, target, backend); - let dylib = t!(fs::read_to_string(&stamp)); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); + let dylib = t!(fs::read_to_string(stamp.path())); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); // change `librustc_codegen_cranelift-xxxxxx.so` to @@ -1514,35 +1530,6 @@ fn copy_codegen_backends_to_sysroot( } } -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -pub fn librustc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}.stamp")) -} - pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, @@ -1664,10 +1651,10 @@ impl Step for Sysroot { ]; let ci_rustc_dir = builder.config.ci_rustc_dir(); builder.cp_link_filtered(&ci_rustc_dir, &sysroot, &|path| { - if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { + if path.extension().is_none_or(|ext| !filtered_extensions.contains(&ext)) { return true; } - if !path.parent().map_or(true, |p| p.ends_with(&suffix)) { + if !path.parent().is_none_or(|p| p.ends_with(&suffix)) { return true; } if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) { @@ -1899,7 +1886,7 @@ impl Step for Assemble { builder.info(&msg); // Link in all dylibs to the libdir - let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let stamp = build_stamp::librustc_stamp(builder, build_compiler, target_compiler.host); let proc_macros = builder .read_stamp_file(&stamp) .into_iter() @@ -2005,7 +1992,7 @@ pub fn add_to_sysroot( builder: &Builder<'_>, sysroot_dst: &Path, sysroot_host_dst: &Path, - stamp: &Path, + stamp: &BuildStamp, ) { let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(sysroot_dst)); @@ -2025,13 +2012,13 @@ pub fn run_cargo( builder: &Builder<'_>, cargo: Cargo, tail_args: Vec<String>, - stamp: &Path, + stamp: &BuildStamp, additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, rlib_only_metadata: bool, ) -> Vec<PathBuf> { // `target_root_dir` looks like $dir/$target/release - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // `target_deps_dir` looks like $dir/$target/release/deps let target_deps_dir = target_root_dir.join("deps"); // `host_root_dir` looks like $dir/release @@ -2184,7 +2171,7 @@ pub fn run_cargo( new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } - t!(fs::write(stamp, &new_contents)); + t!(fs::write(stamp.path(), &new_contents)); deps.into_iter().map(|(d, _)| d).collect() } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 8bfa9cc94b3..8ca087f9941 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,6 +23,7 @@ use crate::core::build_steps::vendor::default_paths_to_vendor; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -47,7 +48,7 @@ fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool { if !builder.config.extended { return false; } - builder.config.tools.as_ref().map_or(true, |tools| tools.contains(tool)) + builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool)) } #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] @@ -410,7 +411,7 @@ impl Step for Rustc { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc")) + .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); builder.install(&rustdoc, &image.join("bin"), 0o755); @@ -584,7 +585,7 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Check that all objects in rlibs for UEFI targets are COFF. This /// ensures that the C compiler isn't producing ELF objects, which would /// not link correctly with the COFF objects. -fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &Path) { +fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) { if !target.ends_with("-uefi") { return; } @@ -615,7 +616,12 @@ fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp } /// Copy stamped files into an image's `target/lib` directory. -fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) { +fn copy_target_libs( + builder: &Builder<'_>, + target: TargetSelection, + image: &Path, + stamp: &BuildStamp, +) { let dst = image.join("lib/rustlib").join(target).join("lib"); let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); @@ -668,7 +674,7 @@ impl Step for Std { tarball.include_target_in_component_name(true); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::libstd_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target); verify_uefi_rlib_format(builder, target, &stamp); copy_target_libs(builder, target, tarball.image_dir(), &stamp); @@ -718,7 +724,7 @@ impl Step for RustcDev { let tarball = Tarball::new(builder, "rustc-dev", &target.triple); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::librustc_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target); copy_target_libs(builder, target, tarball.image_dir(), &stamp); let src_files = &["Cargo.lock"]; @@ -1592,15 +1598,9 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - - for tool in &[ - "clippy", - "rustfmt", - "rust-analyzer", - "rust-docs", - "miri", - "rustc-codegen-cranelift", - ] { + prepare("clippy"); + prepare("rust-analyzer"); + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1640,8 +1640,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rustfmt" { - "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1661,7 +1659,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1779,24 +1777,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } - if built_tools.contains("rustfmt") { - command(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rustfmt") - .args(heat_flags) - .arg("-cg") - .arg("RustFmtGroup") - .arg("-dr") - .arg("RustFmt") - .arg("-var") - .arg("var.RustFmtDir") - .arg("-out") - .arg(exe.join("RustFmtGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")) - .run(builder); - } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1868,9 +1848,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } - if built_tools.contains("rustfmt") { - cmd.arg("-dRustFmtDir=rustfmt"); - } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1897,9 +1874,6 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } - if built_tools.contains("rustfmt") { - candle("RustFmtGroup.wxs".as_ref()); - } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1938,9 +1912,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } - if built_tools.contains("rustfmt") { - cmd.arg("RustFmtGroup.wixobj"); - } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index f15e0f38e69..40c908c3fa9 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -11,8 +11,9 @@ use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use crate::core::builder::Builder; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; -use crate::utils::helpers::{self, program_out_of_date, t}; +use crate::utils::helpers::{self, t}; #[must_use] enum RustfmtStatus { @@ -55,8 +56,8 @@ fn rustfmt( } } -fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> { - let stamp_file = build.out.join("rustfmt.stamp"); +fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { + let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); let mut cmd = command(build.initial_rustfmt()?); cmd.arg("--version"); @@ -73,7 +74,7 @@ fn verify_rustfmt_version(build: &Builder<'_>) -> bool { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return false; }; - !program_out_of_date(&stamp_file, &version) + stamp_file.add_stamp(version).is_up_to_date() } /// Updates the last rustfmt version used. @@ -81,7 +82,7 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file, version)) + t!(std::fs::write(stamp_file.path(), version)) } /// Returns the Rust files modified between the `merge-base` of HEAD and @@ -114,6 +115,9 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } else { println!("fmt: {verb} {len} {adjective}files"); } + if len > 1000 && !CiEnv::is_ci() { + println!("hint: if this number seems too high, try running `git fetch origin master"); + } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index b950bec11fd..563b715fa64 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,14 +12,15 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; -use crate::utils::helpers::{self, HashStamp, t}; -use crate::{Kind, generate_smart_stamp_hash}; +use crate::utils::helpers::{self, t}; pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, out_dir: PathBuf, install_dir: PathBuf, root: PathBuf, @@ -54,18 +55,17 @@ pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> Gc ) }); - let stamp = out_dir.join("gcc-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the GCC submodule commit hash. \ Assuming that an GCC rebuild is not necessary.", ); builder.info(&format!( "To force GCC to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return GccBuildStatus::AlreadyBuilt; diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index b6862c2d5c4..4f96d67a5bd 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -307,7 +307,7 @@ impl Step for Src { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let config = &run.builder.config; - let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src")); + let cond = config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src")); run.path("src").default_condition(cond) } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index be5b4057051..cf55fff4078 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -8,23 +8,23 @@ //! LLVM and compiler-rt are essentially just wired up to everything else to //! ensure that they're always in place if needed. -use std::env; use std::env::consts::EXE_EXTENSION; use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::OnceLock; +use std::{env, fs}; use build_helper::ci::CiEnv; use build_helper::git::get_closest_merge_commit; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, HashStamp, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, Kind, generate_smart_stamp_hash}; +use crate::{CLang, GitRepo, Kind}; #[derive(Clone)] pub struct LlvmResult { @@ -36,7 +36,7 @@ pub struct LlvmResult { } pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, res: LlvmResult, out_dir: PathBuf, root: String, @@ -135,18 +135,17 @@ pub fn prebuilt_llvm_config( ) }); - let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the LLVM submodule commit hash. \ Assuming that an LLVM rebuild is not necessary.", ); builder.info(&format!( "To force LLVM to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return LlvmBuildStatus::AlreadyBuilt(res); @@ -922,18 +921,17 @@ impl Step for Enzyme { }); let out_dir = builder.enzyme_out(target); - let stamp = out_dir.join("enzyme-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the Enzyme submodule commit hash. \ Assuming that an Enzyme rebuild is not necessary.", ); builder.info(&format!( "To force Enzyme to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return out_dir; @@ -1016,8 +1014,9 @@ impl Step for Lld { } let out_dir = builder.lld_out(target); - let done_stamp = out_dir.join("lld-finished-building"); - if done_stamp.exists() { + + let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld"); + if lld_stamp.path().exists() { return out_dir; } @@ -1092,7 +1091,7 @@ impl Step for Lld { cfg.build(); - t!(File::create(&done_stamp)); + t!(lld_stamp.write()); out_dir } } @@ -1122,25 +1121,32 @@ impl Step for Sanitizers { let out_dir = builder.native_dir(self.target).join("sanitizers"); let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel); - if runtimes.is_empty() { + + if builder.config.dry_run() || runtimes.is_empty() { return runtimes; } let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); - if builder.config.dry_run() { - return runtimes; - } - let stamp = out_dir.join("sanitizers-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new(); + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { + generate_smart_stamp_hash( + builder, + &builder.config.src.join("src/llvm-project/compiler-rt"), + builder.in_tree_llvm_info.sha().unwrap_or_default(), + ) + }); + + let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info(&format!( "Rebuild sanitizers by removing the file `{}`", - stamp.path.display() + stamp.path().display() )); } + return runtimes; } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 64bcb0b85f4..7d4404fa97b 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -21,16 +21,18 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ self, LldThreads, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, DocTests, GitRepo, Mode, envify}; +use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; +/// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { path: PathBuf, @@ -43,13 +45,21 @@ impl Step for CrateBootstrap { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // This step is responsible for several different tool paths. By default + // it will test all of them, but requesting specific tools on the + // command-line (e.g. `./x test suggest-tests`) will test only the + // specified tools. run.path("src/tools/jsondoclint") .path("src/tools/suggest-tests") .path("src/tools/replace-version-placeholder") + // We want `./x test tidy` to _run_ the tidy tool, not its tests. + // So we need a separate alias to test the tidy tool itself. .alias("tidyselftest") } fn make_run(run: RunConfig<'_>) { + // Create and ensure a separate instance of this step for each path + // that was selected on the command-line (or selected by default). for path in run.paths { let path = path.assert_single_path().path.clone(); run.builder.ensure(CrateBootstrap { host: run.target, path }); @@ -60,6 +70,8 @@ impl Step for CrateBootstrap { let bootstrap_host = builder.config.build; let compiler = builder.compiler(0, bootstrap_host); let mut path = self.path.to_str().unwrap(); + + // Map alias `tidyselftest` back to the actual crate path of tidy. if path == "tidyselftest" { path = "src/tools/tidy"; } @@ -212,6 +224,9 @@ impl Step for HtmlCheck { } } +/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out +/// some representative crate repositories and runs `cargo test` on them, in +/// order to test cargo. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargotest { stage: u32, @@ -257,6 +272,7 @@ impl Step for Cargotest { } } +/// Runs `cargo test` for cargo itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { stage: u32, @@ -385,6 +401,7 @@ impl Step for RustAnalyzer { } } +/// Runs `cargo test` for rustfmt. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { stage: u32, @@ -528,7 +545,7 @@ impl Step for Miri { // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot); + build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot); } // Run `cargo test`. @@ -589,6 +606,8 @@ impl Step for Miri { } } +/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri` +/// works and that libtest works under miri. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CargoMiri { target: TargetSelection, @@ -963,7 +982,7 @@ impl Step for RustdocGUI { let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); let out_dir = builder.test_out(self.target).join("rustdoc-gui"); - builder.clear_if_dirty(&out_dir, &builder.rustdoc(self.compiler)); + build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler)); if let Some(src) = builder.config.src.to_str() { cmd.arg("--rust-src").arg(src); @@ -1020,6 +1039,10 @@ impl Step for RustdocGUI { } } +/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style +/// problems in the repository. +/// +/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Tidy; @@ -1185,53 +1208,6 @@ macro_rules! test { }; } -/// Declares an alias for running the [`Coverage`] tests in only one mode. -/// Adapted from [`test`]. -macro_rules! coverage_test_alias { - ( - $( #[$attr:meta] )* // allow docstrings and attributes - $name:ident { - alias_and_mode: $alias_and_mode:expr, // &'static str - default: $default:expr, // bool - only_hosts: $only_hosts:expr // bool - $( , )? // optional trailing comma - } - ) => { - $( #[$attr] )* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, - } - - impl $name { - const MODE: &'static str = $alias_and_mode; - } - - impl Step for $name { - type Output = (); - const DEFAULT: bool = $default; - const ONLY_HOSTS: bool = $only_hosts; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Register the mode name as a command-line alias. - // This allows `x test coverage-map` and `x test coverage-run`. - run.alias($alias_and_mode) - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - - run.builder.ensure($name { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE); - } - } - }; -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct RunMakeSupport { pub compiler: Compiler, @@ -1277,6 +1253,8 @@ impl Step for RunMakeSupport { } } +/// Runs `cargo test` on the `src/tools/run-make-support` crate. +/// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRunMakeSupport { host: TargetSelection, @@ -1473,99 +1451,96 @@ impl Step for RunMake { test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly", default: true }); -/// Coverage tests are a bit more complicated than other test suites, because -/// we want to run the same set of test files in multiple different modes, -/// in a way that's convenient and flexible when invoked manually. -/// -/// This combined step runs the specified tests (or all of `tests/coverage`) -/// in both "coverage-map" and "coverage-run" modes. -/// -/// Used by: -/// - `x test coverage` -/// - `x test tests/coverage` -/// - `x test tests/coverage/trivial.rs` (etc) -/// -/// (Each individual mode also has its own step that will run the tests in -/// just that mode.) -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Runs the coverage test suite at `tests/coverage` in some or all of the +/// coverage test modes. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, + pub mode: &'static str, } impl Coverage { const PATH: &'static str = "tests/coverage"; const SUITE: &'static str = "coverage"; - - /// Runs the coverage test suite (or a user-specified subset) in one mode. - /// - /// This same function is used by the multi-mode step ([`Coverage`]) and by - /// the single-mode steps ([`CoverageMap`] and [`CoverageRun`]), to help - /// ensure that they all behave consistently with each other, regardless of - /// how the coverage tests have been invoked. - fn run_coverage_tests( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - mode: &'static str, - ) { - // Like many other test steps, we delegate to a `Compiletest` step to - // actually run the tests. (See `test_definitions!`.) - builder.ensure(Compiletest { - compiler, - target, - mode, - suite: Self::SUITE, - path: Self::PATH, - compare_mode: None, - }); - } + const ALL_MODES: &[&str] = &["coverage-map", "coverage-run"]; } impl Step for Coverage { type Output = (); - /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. - const DEFAULT: bool = false; - /// When manually invoked, try to run as much as possible. + const DEFAULT: bool = true; /// Compiletest will automatically skip the "coverage-run" tests if necessary. const ONLY_HOSTS: bool = false; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Take responsibility for command-line paths within `tests/coverage`. - run.suite_path(Self::PATH) + fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { + // Support various invocation styles, including: + // - `./x test coverage` + // - `./x test tests/coverage/trivial.rs` + // - `./x test coverage-map` + // - `./x test coverage-run -- tests/coverage/trivial.rs` + run = run.suite_path(Self::PATH); + for mode in Self::ALL_MODES { + run = run.alias(mode); + } + run } fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + let target = run.target; + + // List of (coverage) test modes that the coverage test suite will be + // run in. It's OK for this to contain duplicates, because the call to + // `Builder::ensure` below will take care of deduplication. + let mut modes = vec![]; + + // From the pathsets that were selected on the command-line (or by default), + // determine which modes to run in. + for path in &run.paths { + match path { + PathSet::Set(_) => { + for mode in Self::ALL_MODES { + if path.assert_single_path().path == Path::new(mode) { + modes.push(mode); + break; + } + } + } + PathSet::Suite(_) => { + modes.extend(Self::ALL_MODES); + break; + } + } + } - run.builder.ensure(Coverage { compiler, target: run.target }); - } + // Skip any modes that were explicitly skipped/excluded on the command-line. + // FIXME(Zalathar): Integrate this into central skip handling somehow? + modes.retain(|mode| !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode))); - fn run(self, builder: &Builder<'_>) { - // Run the specified coverage tests (possibly all of them) in both modes. - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE); - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE); - } -} + // FIXME(Zalathar): Make these commands skip all coverage tests, as expected: + // - `./x test --skip=tests` + // - `./x test --skip=tests/coverage` + // - `./x test --skip=coverage` + // Skip handling currently doesn't have a way to know that skipping the coverage + // suite should also skip the `coverage-map` and `coverage-run` aliases. -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-map" mode only. - /// Used by `x test` and `x test coverage-map`. - CoverageMap { - alias_and_mode: "coverage-map", - default: true, - only_hosts: false, + for mode in modes { + run.builder.ensure(Coverage { compiler, target, mode }); + } } -} -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-run" mode only. - /// Used by `x test` and `x test coverage-run`. - CoverageRun { - alias_and_mode: "coverage-run", - default: true, - // Compiletest knows how to automatically skip these tests when cross-compiling, - // but skipping the whole step here makes it clearer that they haven't run at all. - only_hosts: true, + + fn run(self, builder: &Builder<'_>) { + let Self { compiler, target, mode } = self; + // Like other compiletest suite test steps, delegate to an internal + // compiletest task to actually run the tests. + builder.ensure(Compiletest { + compiler, + target, + mode, + suite: Self::SUITE, + path: Self::PATH, + compare_mode: None, + }); } } @@ -1663,7 +1638,10 @@ impl Step for Compiletest { return; } - if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() { + if builder.top_stage == 0 + && env::var("COMPILETEST_FORCE_STAGE0").is_err() + && self.mode != "js-doc-test" + { eprintln!("\ ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail HELP: to test the compiler, use `--stage 1` instead @@ -2284,10 +2262,8 @@ impl BookTest { &[], ); - let stamp = builder - .cargo_out(compiler, mode, target) - .join(PathBuf::from(dep).file_name().unwrap()) - .with_extension("stamp"); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); let directories = output_paths @@ -2516,6 +2492,10 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> } } +/// Runs `cargo test` for the compiler crates in `compiler/`. +/// +/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`, +/// which have their own separate test steps.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateLibrustc { compiler: Compiler, @@ -2544,6 +2524,7 @@ impl Step for CrateLibrustc { fn run(self, builder: &Builder<'_>) { builder.ensure(compile::Std::new(self.compiler, self.target)); + // To actually run the tests, delegate to a copy of the `Crate` step. builder.ensure(Crate { compiler: self.compiler, target: self.target, @@ -2669,6 +2650,13 @@ fn prepare_cargo_test( cargo } +/// Runs `cargo test` for standard library crates. +/// +/// (Also used internally to run `cargo test` for compiler crates.) +/// +/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible +/// step for testing standard library crates, and an internal step used for both +/// library crates and compiler crates. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Crate { pub compiler: Compiler, @@ -3602,6 +3590,10 @@ impl Step for CodegenGCC { } } +/// Test step that does two things: +/// - Runs `cargo test` for the `src/etc/test-float-parse` tool. +/// - Invokes the `test-float-parse` tool to test the standard library's +/// float parsing routines. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestFloatParse { path: PathBuf, @@ -3675,6 +3667,9 @@ impl Step for TestFloatParse { } } +/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode, +/// which verifies that `license-metadata.json` is up-to-date and therefore +/// running the tool normally would not update anything. #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 432fbb8d636..2692a129ef3 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -7,6 +7,7 @@ use crate::core::build_steps::tool::SourceType; use crate::core::build_steps::{compile, test}; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; +use crate::utils::build_stamp; use crate::utils::helpers::{ self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, }; @@ -454,7 +455,7 @@ impl Builder<'_> { // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, // so we need to explicitly clear out if they've been updated. for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); + build_stamp::clear_if_dirty(self, &out_dir, &backend); } if cmd_kind == Kind::Doc { @@ -471,7 +472,7 @@ impl Builder<'_> { _ => panic!("doc mode {mode:?} not expected"), }; let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); + build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } let profile_var = |name: &str| { @@ -624,8 +625,6 @@ impl Builder<'_> { // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); hostflags.arg("--check-cfg=cfg(bootstrap)"); - // #[cfg(bootstrap)] as we are transition `test` to userspace cfg - hostflags.arg("--check-cfg=cfg(test)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -765,7 +764,7 @@ impl Builder<'_> { // Only clear out the directory if we're compiling std; otherwise, we // should let Cargo take care of things for us (via depdep info) if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } let rustdoc_path = match cmd_kind { @@ -1206,10 +1205,7 @@ impl Builder<'_> { // so that it'll be available when downstream consumers of std try to use it. rustflags.arg("-Zinline-mir-preserve-debug"); - // FIXME: always pass this after the next `#[cfg(bootstrap)]` update. - if compiler.stage != 0 { - rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); - } + rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); } Cargo { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0660642c4d4..17de1762427 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -944,8 +944,6 @@ impl<'a> Builder<'a> { test::Ui, test::Crashes, test::Coverage, - test::CoverageMap, - test::CoverageRun, test::MirOpt, test::Codegen, test::CodegenUnits, @@ -1445,7 +1443,7 @@ impl<'a> Builder<'a> { let mut stack = self.stack.borrow_mut(); for stack_step in stack.iter() { // should skip - if stack_step.downcast_ref::<S>().map_or(true, |stack_step| *stack_step != step) { + if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) { continue; } let mut out = String::new(); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5769198afac..0c27597083d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -828,3 +828,36 @@ fn test_test_compiler() { assert_eq!((compiler, cranelift, gcc), (true, false, false)); } + +#[test] +fn test_test_coverage() { + struct Case { + cmd: &'static [&'static str], + expected: &'static [&'static str], + } + let cases = &[ + Case { cmd: &["test"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage-map"], expected: &["coverage-map"] }, + Case { cmd: &["test", "coverage-run"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests/coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=coverage-map"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage-run"], expected: &["coverage-map"] }, + Case { cmd: &["test", "--skip=coverage-map", "--skip=coverage-run"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests"], expected: &[] }, + ]; + + for &Case { cmd, expected } in cases { + // Print each test case so that if one fails, the most recently printed + // case is the one that failed. + println!("Testing case: {cmd:?}"); + let cmd = cmd.iter().copied().map(str::to_owned).collect::<Vec<_>>(); + let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let mut cache = run_build(&config.paths.clone(), config); + + let modes = + cache.all::<test::Coverage>().iter().map(|(step, ())| step.mode).collect::<Vec<_>>(); + assert_eq!(modes, expected); + } +} diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2f11ad469..026380a46c6 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -634,6 +634,7 @@ pub struct Target { pub runner: Option<String>, pub no_std: bool, pub codegen_backends: Option<Vec<String>>, + pub optimized_compiler_builtins: Option<bool>, } impl Target { @@ -1125,7 +1126,7 @@ impl<'de> Deserialize<'de> for LldMode { match v { "external" => Ok(LldMode::External), "self-contained" => Ok(LldMode::SelfContained), - _ => Err(E::custom("unknown mode {v}")), + _ => Err(E::custom(format!("unknown mode {v}"))), } } } @@ -1219,6 +1220,7 @@ define_config! { no_std: Option<bool> = "no-std", codegen_backends: Option<Vec<String>> = "codegen-backends", runner: Option<String> = "runner", + optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins", } } @@ -2096,6 +2098,7 @@ impl Config { target.sanitizers = cfg.sanitizers; target.profiler = cfg.profiler; target.rpath = cfg.rpath; + target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; if let Some(ref backends) = cfg.codegen_backends { let available_backends = ["llvm", "cranelift", "gcc"]; @@ -2609,6 +2612,13 @@ impl Config { self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath) } + pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { + self.target_config + .get(&target) + .and_then(|t| t.optimized_compiler_builtins) + .unwrap_or(self.optimized_compiler_builtins) + } + pub fn llvm_enabled(&self, target: TargetSelection) -> bool { self.codegen_backends(target).contains(&"llvm".to_owned()) } @@ -3162,6 +3172,9 @@ fn check_incompatible_options_for_ci_rustc( let profiler = &ci_cfg.profiler; err!(current_cfg.profiler, profiler, "build"); + + let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; + err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); } } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 24f932a1724..c5e578ff351 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -120,6 +120,7 @@ fn override_toml() { "--set=change-id=1".to_owned(), "--set=rust.lto=fat".to_owned(), "--set=rust.deny-warnings=false".to_owned(), + "--set=build.optimized-compiler-builtins=true".to_owned(), "--set=build.gdb=\"bar\"".to_owned(), "--set=build.tools=[\"cargo\"]".to_owned(), "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), @@ -127,6 +128,7 @@ fn override_toml() { "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), ]), |&_| { toml::from_str( @@ -167,6 +169,7 @@ runner = "x86_64-runner" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); assert!(!config.deny_warnings, "setting boolean value"); + assert!(config.optimized_compiler_builtins, "setting boolean value"); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -193,7 +196,11 @@ runner = "x86_64-runner" ..Default::default() }; let darwin = TargetSelection::from_user("aarch64-apple-darwin"); - let darwin_values = Target { runner: Some("apple".into()), ..Default::default() }; + let darwin_values = Target { + runner: Some("apple".into()), + optimized_compiler_builtins: Some(false), + ..Default::default() + }; assert_eq!( config.target_config, [(x86_64, x86_64_values), (aarch64, aarch64_values), (darwin, darwin_values)] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index b5f7ed53131..c477bdb829a 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -10,8 +10,9 @@ use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{check_run, exe, hex_encode, move_file, program_out_of_date}; +use crate::utils::helpers::{check_run, exe, hex_encode, move_file}; use crate::{Config, t}; static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock<bool> = OnceLock::new(); @@ -46,7 +47,7 @@ impl Config { self.verbose > 0 } - pub(crate) fn create(&self, path: &Path, s: &str) { + pub(crate) fn create<P: AsRef<Path>>(&self, path: P, s: &str) { if self.dry_run() { return; } @@ -426,9 +427,10 @@ impl Config { let version = &self.stage0_metadata.compiler.version; let host = self.build; - let clippy_stamp = self.initial_sysroot.join(".clippy-stamp"); + let clippy_stamp = + BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date); let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host)); - if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, date) { + if cargo_clippy.exists() && clippy_stamp.is_up_to_date() { return cargo_clippy; } @@ -439,7 +441,7 @@ impl Config { self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host))); } - self.create(&clippy_stamp, date); + t!(clippy_stamp.write()); cargo_clippy } @@ -460,8 +462,8 @@ impl Config { let host = self.build; let bin_root = self.out.join(host).join("rustfmt"); let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { return Some(rustfmt_path); } @@ -492,7 +494,7 @@ impl Config { } } - self.create(&rustfmt_stamp, &channel); + t!(rustfmt_stamp.write()); Some(rustfmt_path) } @@ -567,10 +569,10 @@ impl Config { ) { let host = self.build.triple; let bin_root = self.out.join(host).join(sysroot); - let rustc_stamp = bin_root.join(".rustc-stamp"); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); if !bin_root.join("bin").join(exe("rustc", self.build)).exists() - || program_out_of_date(&rustc_stamp, stamp_key) + || !rustc_stamp.is_up_to_date() { if bin_root.exists() { t!(fs::remove_dir_all(&bin_root)); @@ -601,7 +603,7 @@ impl Config { } } - t!(fs::write(rustc_stamp, stamp_key)); + t!(rustc_stamp.write()); } } @@ -728,10 +730,10 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_stamp = llvm_root.join(".llvm-stamp"); let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); - let key = format!("{}{}", llvm_sha, self.llvm_assertions); - if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { + let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); + let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); + if !llvm_stamp.is_up_to_date() && !self.dry_run() { self.download_ci_llvm(&llvm_sha); if self.should_fix_bins_and_dylibs() { @@ -764,7 +766,7 @@ download-rustc = false } } - t!(fs::write(llvm_stamp, key)); + t!(llvm_stamp.write()); } if let Some(config_path) = &self.config { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 8405c22aff0..ebe2c332258 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -19,27 +19,23 @@ use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; use std::time::SystemTime; -use std::{env, io, str}; +use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; -use sha2::digest::Digest; use termcolor::{ColorChoice, StandardStream, WriteColor}; +use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; -use utils::helpers::hex_encode; use crate::core::builder; -use crate::core::builder::{Builder, Kind}; +use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; mod core; mod utils; @@ -77,8 +73,6 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; #[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), - // #[cfg(bootstrap)] to be removed when Cargo is updated - (None, "test", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), (Some(Mode::ToolRustc), "llvm_enzyme", None), @@ -600,24 +594,6 @@ impl Build { self.metrics.persist(self); } - /// Clear out `dir` if `input` is newer. - /// - /// After this executes, it will also ensure that `dir` exists. - fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { - let stamp = dir.join(".stamp"); - let mut cleared = false; - if mtime(&stamp) < mtime(input) { - self.verbose(|| println!("Dirty - {}", dir.display())); - let _ = fs::remove_dir_all(dir); - cleared = true; - } else if stamp.exists() { - return cleared; - } - t!(fs::create_dir_all(dir)); - t!(File::create(stamp)); - cleared - } - fn rust_info(&self) -> &GitInfo { &self.config.rust_info } @@ -1624,21 +1600,21 @@ Executed at: {executed_at}"#, ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { + fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run() { return Vec::new(); } - if !stamp.exists() { + if !stamp.path().exists() { eprintln!( "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?", - stamp.display() + stamp.path().display() ); crate::exit!(1); } let mut paths = Vec::new(); - let contents = t!(fs::read(stamp), &stamp); + let contents = t!(fs::read(stamp.path()), stamp.path()); // This is the method we use for extracting paths from the stamp file passed to us. See // run_cargo for more information (in compile.rs). for part in contents.split(|b| *b == 0) { @@ -1924,52 +1900,6 @@ fn envify(s: &str) -> String { .collect() } -/// Computes a hash representing the state of a repository/submodule and additional input. -/// -/// It uses `git diff` for the actual changes, and `git status` for including the untracked -/// files in the specified directory. The additional input is also incorporated into the -/// computation of the hash. -/// -/// # Parameters -/// -/// - `dir`: A reference to the directory path of the target repository/submodule. -/// - `additional_input`: An additional input to be included in the hash. -/// -/// # Panics -/// -/// In case of errors during `git` command execution (e.g., in tarball sources), default values -/// are used to prevent panics. -pub fn generate_smart_stamp_hash( - builder: &Builder<'_>, - dir: &Path, - additional_input: &str, -) -> String { - let diff = helpers::git(Some(dir)) - .allow_failure() - .arg("diff") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let status = helpers::git(Some(dir)) - .allow_failure() - .arg("status") - .arg("--porcelain") - .arg("-z") - .arg("--untracked-files=normal") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(diff); - hasher.update(status); - hasher.update(additional_input); - - hex_encode(hasher.finalize().as_slice()) -} - /// Ensures that the behavior dump directory is properly initialized. pub fn prepare_behaviour_dump_dir(build: &Build) { static INITIALIZED: OnceLock<bool> = OnceLock::new(); diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs new file mode 100644 index 00000000000..f43d860893f --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -0,0 +1,204 @@ +//! Module for managing build stamp files. +//! +//! Contains the core implementation of how bootstrap utilizes stamp files on build processes. + +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +use sha2::digest::Digest; + +use crate::core::builder::Builder; +use crate::core::config::TargetSelection; +use crate::utils::helpers::{hex_encode, mtime}; +use crate::{Compiler, Mode, helpers, t}; + +#[cfg(test)] +mod tests; + +/// Manages a stamp file to track build state. The file is created in the given +/// directory and can have custom content and name. +#[derive(Clone)] +pub struct BuildStamp { + path: PathBuf, + stamp: String, +} + +impl BuildStamp { + /// Creates a new `BuildStamp` for a given directory. + /// + /// By default, stamp will be an empty file named `.stamp` within the specified directory. + pub fn new(dir: &Path) -> Self { + // Avoid using `is_dir()` as the directory may not exist yet. + // It is more appropriate to assert that the path is not a file. + assert!(!dir.is_file(), "can't be a file path"); + Self { path: dir.join(".stamp"), stamp: String::new() } + } + + /// Returns path of the stamp file. + pub fn path(&self) -> &Path { + &self.path + } + + /// Returns the value of the stamp. + /// + /// Note that this is empty by default and is populated using `BuildStamp::add_stamp`. + /// It is not read from an actual file, but rather it holds the value that will be used + /// when `BuildStamp::write` is called. + pub fn stamp(&self) -> &str { + &self.stamp + } + + /// Adds specified stamp content to the current value. + /// + /// This method can be used incrementally e.g., `add_stamp("x").add_stamp("y").add_stamp("z")`. + pub fn add_stamp<S: ToString>(mut self, stamp: S) -> Self { + self.stamp.push_str(&stamp.to_string()); + self + } + + /// Adds a prefix to stamp's name. + /// + /// Prefix cannot start or end with a dot (`.`). + pub fn with_prefix(mut self, prefix: &str) -> Self { + assert!( + !prefix.starts_with('.') && !prefix.ends_with('.'), + "prefix can not start or end with '.'" + ); + + let stamp_filename = self.path.file_name().unwrap().to_str().unwrap(); + let stamp_filename = stamp_filename.strip_prefix('.').unwrap_or(stamp_filename); + self.path.set_file_name(format!(".{prefix}-{stamp_filename}")); + + self + } + + /// Removes the stamp file if it exists. + pub fn remove(&self) -> io::Result<()> { + match fs::remove_file(&self.path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(()) + } else { + Err(e) + } + } + } + } + + /// Creates the stamp file. + pub fn write(&self) -> io::Result<()> { + fs::write(&self.path, &self.stamp) + } + + /// Checks if the stamp file is up-to-date. + /// + /// It is considered up-to-date if file content matches with the stamp string. + pub fn is_up_to_date(&self) -> bool { + match fs::read(&self.path) { + Ok(h) => self.stamp.as_bytes() == h.as_slice(), + Err(e) if e.kind() == io::ErrorKind::NotFound => false, + Err(e) => { + panic!("failed to read stamp file `{}`: {}", self.path.display(), e); + } + } + } +} + +/// Clear out `dir` if `input` is newer. +/// +/// After this executes, it will also ensure that `dir` exists. +pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { + let stamp = BuildStamp::new(dir); + let mut cleared = false; + if mtime(stamp.path()) < mtime(input) { + builder.verbose(|| println!("Dirty - {}", dir.display())); + let _ = fs::remove_dir_all(dir); + cleared = true; + } else if stamp.path().exists() { + return cleared; + } + t!(fs::create_dir_all(dir)); + t!(fs::File::create(stamp.path())); + cleared +} + +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +pub fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: &str, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Codegen, target)) + .with_prefix(&format!("librustc_codegen_{backend}")) +} + +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +pub fn libstd_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd") +} + +/// Cargo's output path for librustc in a given stage, compiled by a particular +/// compiler for the specified target. +pub fn librustc_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc") +} + +/// Computes a hash representing the state of a repository/submodule and additional input. +/// +/// It uses `git diff` for the actual changes, and `git status` for including the untracked +/// files in the specified directory. The additional input is also incorporated into the +/// computation of the hash. +/// +/// # Parameters +/// +/// - `dir`: A reference to the directory path of the target repository/submodule. +/// - `additional_input`: An additional input to be included in the hash. +/// +/// # Panics +/// +/// In case of errors during `git` command execution (e.g., in tarball sources), default values +/// are used to prevent panics. +pub fn generate_smart_stamp_hash( + builder: &Builder<'_>, + dir: &Path, + additional_input: &str, +) -> String { + let diff = helpers::git(Some(dir)) + .allow_failure() + .arg("diff") + .arg(".") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let status = helpers::git(Some(dir)) + .allow_failure() + .arg("status") + .arg(".") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(diff); + hasher.update(status); + hasher.update(additional_input); + + hex_encode(hasher.finalize().as_slice()) +} diff --git a/src/bootstrap/src/utils/build_stamp/tests.rs b/src/bootstrap/src/utils/build_stamp/tests.rs new file mode 100644 index 00000000000..2d7d1f71246 --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp/tests.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use crate::{BuildStamp, Config, Flags}; + +fn temp_dir() -> PathBuf { + let config = + Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + config.tempdir() +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix(".invalid"); +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix2() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix("invalid."); +} + +#[test] +fn test_is_up_to_date() { + let dir = temp_dir(); + + let mut build_stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + build_stamp.write().unwrap(); + + assert!( + build_stamp.is_up_to_date(), + "Expected stamp file to be up-to-date, but contents do not match the expected value." + ); + + build_stamp.stamp = "dummy value".to_owned(); + assert!( + !build_stamp.is_up_to_date(), + "Stamp should no longer be up-to-date as we changed its content right above." + ); + + build_stamp.remove().unwrap(); +} + +#[test] +fn test_with_prefix() { + let dir = temp_dir(); + + let stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + assert_eq!(stamp.path.file_name().unwrap(), ".stamp"); + + let stamp = stamp.with_prefix("test"); + let expected_filename = ".test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); + + let stamp = stamp.with_prefix("extra-prefix"); + let expected_filename = ".extra-prefix-test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 8fd6c8aa23a..85d17c8fa50 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -325,4 +325,14 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Removed `rust.parallel-compiler` as it was deprecated in #132282 long time ago.", }, + ChangeInfo { + change_id: 135326, + severity: ChangeSeverity::Warning, + summary: "It is now possible to configure `optimized-compiler-builtins` for per target.", + }, + ChangeInfo { + change_id: 135281, + severity: ChangeSeverity::Warning, + summary: "Some stamp names in the build artifacts may have changed slightly (e.g., from `llvm-finished-building` to `.llvm-stamp`).", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 530d760a584..60216395c2f 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -120,7 +120,7 @@ impl BootstrapCommand { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } - #[must_use] + #[allow(dead_code)] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } @@ -275,7 +275,7 @@ impl CommandOutput { !self.is_success() } - #[must_use] + #[allow(dead_code)] pub fn status(&self) -> Option<ExitStatus> { match self.status { CommandStatus::Finished(status) => Some(status), diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 3657d9b3112..516c314024b 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -16,6 +16,7 @@ use object::read::archive::ArchiveFile; use crate::LldMode; use crate::core::builder::Builder; use crate::core::config::{Config, TargetSelection}; +use crate::utils::exec::{BootstrapCommand, command}; pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var}; #[cfg(test)] @@ -45,10 +46,8 @@ macro_rules! t { } }; } -pub use t; - -use crate::utils::exec::{BootstrapCommand, command}; +pub use t; pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } @@ -148,14 +147,6 @@ impl Drop for TimeIt { } } -/// Used for download caching -pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { - if !stamp.exists() { - return true; - } - t!(fs::read_to_string(stamp)) != key -} - /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { @@ -181,10 +172,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< /// copy and remove the file otherwise pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { match fs::rename(&from, &to) { - // FIXME: Once `ErrorKind::CrossesDevices` is stabilized use - // if e.kind() == io::ErrorKind::CrossesDevices { - #[cfg(unix)] - Err(e) if e.raw_os_error() == Some(libc::EXDEV) => { + Err(e) if e.kind() == io::ErrorKind::CrossesDevices => { std::fs::copy(&from, &to)?; std::fs::remove_file(&from) } @@ -601,41 +589,3 @@ pub fn set_file_times<P: AsRef<Path>>(path: P, times: fs::FileTimes) -> io::Resu }; f.set_times(times) } - -pub struct HashStamp { - pub path: PathBuf, - pub hash: Option<Vec<u8>>, -} - -impl HashStamp { - pub fn new(path: PathBuf, hash: Option<&str>) -> Self { - HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } - } - - pub fn is_done(&self) -> bool { - match fs::read(&self.path) { - Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), - Err(e) if e.kind() == io::ErrorKind::NotFound => false, - Err(e) => { - panic!("failed to read stamp file `{}`: {}", self.path.display(), e); - } - } - } - - pub fn remove(&self) -> io::Result<()> { - match fs::remove_file(&self.path) { - Ok(()) => Ok(()), - Err(e) => { - if e.kind() == io::ErrorKind::NotFound { - Ok(()) - } else { - Err(e) - } - } - } - } - - pub fn write(&self) -> io::Result<()> { - fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) - } -} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 613286cfaa8..9030ca2820a 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -1,10 +1,10 @@ -use std::fs::{self, File, remove_file}; +use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, set_file_times, - submodule_path_of, symlink_dir, + check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, + symlink_dir, }; use crate::{Config, Flags}; @@ -58,22 +58,6 @@ fn test_check_cfg_arg() { } #[test] -fn test_program_out_of_date() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); - let tempfile = config.tempdir().join(".tmp-stamp-file"); - File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); - assert!(tempfile.exists()); - - // up-to-date - assert!(!program_out_of_date(&tempfile, "dummy value")); - // out-of-date - assert!(program_out_of_date(&tempfile, "")); - - remove_file(tempfile).unwrap(); -} - -#[test] fn test_symlink_dir() { let config = Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index b5f5e2ba6dc..ea56932b404 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -2,6 +2,7 @@ //! support for a wide range of tasks and operations such as caching, tarballs, release //! channels, job management, etc. +pub(crate) mod build_stamp; pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod change_tracker; @@ -9,10 +10,12 @@ pub(crate) mod channel; pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; -#[cfg(feature = "build-metrics")] -pub(crate) mod metrics; pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; + +#[cfg(feature = "build-metrics")] +pub(crate) mod metrics; + #[cfg(test)] mod tests; diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 2aad5650fa8..01bac1498c2 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -129,8 +129,19 @@ pub fn get_closest_merge_commit( git.current_dir(git_dir); } + let channel = include_str!("../../ci/channel"); + let merge_base = { - if CiEnv::is_ci() { + if CiEnv::is_ci() && + // FIXME: When running on rust-lang managed CI and it's not a nightly build, + // `git_upstream_merge_base` fails with an error message similar to this: + // ``` + // called `Result::unwrap()` on an `Err` value: "command did not execute successfully: + // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" + // ``` + // Investigate and resolve this issue instead of skipping it like this. + (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) + { git_upstream_merge_base(config, git_dir).unwrap() } else { // For non-CI environments, ignore rust-lang/rust upstream as it usually gets diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 508b7b40c01..2f52ff5a99a 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -1,29 +1,30 @@ # Docker images for CI This folder contains a bunch of docker images used by the continuous integration -(CI) of Rust. An script is accompanied (`run.sh`) with these images to actually -execute them. To test out an image execute: +(CI) of Rust. A script is accompanied (`run.sh`) with these images to actually +execute them. -``` -./src/ci/docker/run.sh $image_name -``` +Note that a single Docker image can be used by multiple CI jobs, so the job name +is the important thing that you should know. You can examine the existing CI jobs in +the [`jobs.yml`](../github-actions/jobs.yml) file. -for example: +To run a specific CI job locally, you can use the following script: ``` -./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local <job-name> ``` -Images will output artifacts in an `obj/$image_name` dir at the root of a repository. Note -that the script will overwrite the contents of this directory. - -To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e.g.: +For example, to run the `x86_64-gnu-llvm-18-1` job: ``` -DEPLOY=1 ./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local x86_64-gnu-llvm-18-1 ``` +The job will output artifacts in an `obj/<image-name>` dir at the root of a repository. Note +that the script will overwrite the contents of this directory. `<image-name>` is set based on the +Docker image executed in the given CI job. + **NOTE**: In CI, the script outputs the artifacts to the `obj` directory, -while locally, to the `obj/$image_name` directory. This is primarily to prevent +while locally, to the `obj/<image-name>` directory. This is primarily to prevent strange linker errors when using multiple Docker images. For some Linux workflows (for example `x86_64-gnu-llvm-18-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-18-3` workflow, you can run the following script: diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile new file mode 100644 index 00000000000..8b0fb0710d4 --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -0,0 +1,100 @@ +# We document platform support for minimum glibc 2.17 and kernel 3.2. +# CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't +# actually use newer APIs in rustc or std without a fallback. It's more +# important that we match glibc for ELF symbol versioning. +FROM centos:7 + +WORKDIR /build + +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + +RUN yum upgrade -y && \ + yum install -y \ + automake \ + bzip2 \ + file \ + gcc \ + gcc-c++ \ + git \ + glibc-devel \ + libedit-devel \ + libstdc++-devel \ + make \ + ncurses-devel \ + openssl-devel \ + patch \ + perl \ + perl-core \ + pkgconfig \ + python3 \ + unzip \ + wget \ + xz \ + zlib-devel \ + && yum clean all + +RUN mkdir -p /rustroot/bin + +ENV PATH=/rustroot/bin:$PATH +ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib +ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig +WORKDIR /tmp +RUN mkdir /home/user +COPY scripts/shared.sh /tmp/ + +# Need at least GCC 5.1 to compile LLVM +COPY scripts/build-gcc.sh /tmp/ +ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=aarch64-unknown-linux-gnu +RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ + +ENV CC=gcc CXX=g++ + +# LLVM 17 needs cmake 3.20 or higher. +COPY scripts/cmake.sh /tmp/ +RUN ./cmake.sh + +# Build LLVM+Clang +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=AArch64 +RUN ./build-clang.sh +ENV CC=clang CXX=clang++ + +# Build zstd to enable `llvm.libzstd`. +COPY scripts/build-zstd.sh /tmp/ +RUN ./build-zstd.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PGO_HOST=aarch64-unknown-linux-gnu +ENV HOSTS=aarch64-unknown-linux-gnu + +ENV CPATH=/usr/include/aarch64-linux-gnu/:$CPATH + +ENV RUST_CONFIGURE_ARGS \ + --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.codegen-units=1 + +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS + +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang +ENV LIBCURL_NO_PKG_CONFIG 1 +ENV DIST_REQUIRE_ALL_TOOLS 1 diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile deleted file mode 100644 index 18972387e34..00000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/aarch64-unknown-linux-gnu/bin - -ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-gcc \ - AR_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-ar \ - CXX_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-g++ - -ENV HOSTS=aarch64-unknown-linux-gnu - -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ - --enable-profiler \ - --enable-sanitizers -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig deleted file mode 100644 index 520b1667c8b..00000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig +++ /dev/null @@ -1,10 +0,0 @@ -CT_CONFIG_VERSION="4" -CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_USE_MIRROR=y -CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_ARCH_ARM=y -CT_ARCH_64=y -CT_KERNEL_LINUX=y -CT_LINUX_V_4_1=y -CT_GLIBC_V_2_17=y -CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 7e946df6163..ea5b208a7c1 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -19,6 +19,7 @@ RUN yum upgrade -y && \ gcc \ gcc-c++ \ git \ + binutils.i686 \ glibc-devel.i686 \ glibc-devel.x86_64 \ libedit-devel \ @@ -46,11 +47,12 @@ ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=i686-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ COPY scripts/cmake.sh /tmp/ @@ -58,7 +60,8 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile index 9ef39189249..9d3be51d037 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile @@ -18,7 +18,7 @@ RUN /scripts/crosstool-ng-build.sh WORKDIR /build RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ +COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh deleted file mode 100644 index dc86dddd464..00000000000 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch index 7ae4469428b..1ae0ecf6cb5 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch @@ -10,7 +10,7 @@ https://sourceware.org/bugzilla/show_bug.cgi?id=28509 And this is the first version of the proposed binutils patch, https://sourceware.org/pipermail/binutils/2021-November/118398.html -After applying the binutils patch, I get the the unexpected error when +After applying the binutils patch, I get the unexpected error when building libgcc, /scratch/nelsonc/riscv-gnu-toolchain/riscv-gcc/libgcc/config/riscv/div.S:42: diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index bbb4fe216a5..dde6fe7f6d0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -47,11 +47,12 @@ ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig # Clang needs to access GCC headers to enable linker plugin LTO WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=x86_64-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ # LLVM 17 needs cmake 3.20 or higher. @@ -60,12 +61,13 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ # Build zstd to enable `llvm.libzstd`. -COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/ +COPY scripts/build-zstd.sh /tmp/ RUN ./build-zstd.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh deleted file mode 100644 index dc86dddd464..00000000000 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e157debfd09..0a58f337d9d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index e7016e7d3c0..092847cdfe0 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 2a09cd54b13..ab749b3fdd5 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -89,8 +89,8 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu # assertions enabled! Therefore, we cannot force download CI rustc. #ENV FORCE_CI_RUSTC 1 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/scripts/build-clang.sh index 3c8123d90de..841a0adb2ab 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/scripts/build-clang.sh @@ -21,6 +21,12 @@ cd clang-build # include path, /rustroot/include, to clang's default include path. INC="/rustroot/include:/usr/include" +GCC_PLUGIN_TARGET=$GCC_BUILD_TARGET +# We build gcc for the i686 job on x86_64 so the plugin will end up under an x86_64 path +if [[ $GCC_PLUGIN_TARGET == "i686-pc-linux-gnu" ]]; then + GCC_PLUGIN_TARGET=x86_64-pc-linux-gnu +fi + # We need compiler-rt for the profile runtime (used later to PGO the LLVM build) # but sanitizers aren't currently building. Since we don't need those, just # disable them. BOLT is used for optimizing LLVM. @@ -34,12 +40,12 @@ hide_output \ -DCOMPILER_RT_BUILD_XRAY=OFF \ -DCOMPILER_RT_BUILD_MEMPROF=OFF \ -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ - -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_TARGETS_TO_BUILD=$LLVM_BUILD_TARGETS \ -DLLVM_INCLUDE_BENCHMARKS=OFF \ -DLLVM_INCLUDE_TESTS=OFF \ -DLLVM_INCLUDE_EXAMPLES=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt;bolt" \ - -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC_VERSION/plugin/include/" \ + -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/$GCC_PLUGIN_TARGET/$GCC_VERSION/plugin/include/" \ -DC_INCLUDE_DIRS="$INC" hide_output make -j$(nproc) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 57d4d338a50..11db5aa811c 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -51,7 +51,9 @@ cd .. rm -rf gcc-build rm -rf gcc-$GCC -# FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, -# but it does look all the way under /rustroot/lib/[...]/32, -# so we can link stuff there to help it out. -ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +if [[ $GCC_BUILD_TARGET == "i686-pc-linux-gnu" ]]; then + # FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, + # but it does look all the way under /rustroot/lib/[...]/32, + # so we can link stuff there to help it out. + ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +fi diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh b/src/ci/docker/scripts/build-gccjit.sh index c565922dcd1..c565922dcd1 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh +++ b/src/ci/docker/scripts/build-gccjit.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh index a3d37ccc311..a3d37ccc311 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh +++ b/src/ci/docker/scripts/build-zstd.sh diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/ci.py index 1f994f0ffd2..b7dac412dbe 100755 --- a/src/ci/github-actions/calculate-job-matrix.py +++ b/src/ci/github-actions/ci.py @@ -1,18 +1,20 @@ #!/usr/bin/env python3 """ -This script serves for generating a matrix of jobs that should -be executed on CI. +This script contains CI functionality. +It can be used to generate a matrix of jobs that should +be executed on CI, or run a specific CI job locally. -It reads job definitions from `src/ci/github-actions/jobs.yml` -and filters them based on the event that happened on CI. +It reads job definitions from `src/ci/github-actions/jobs.yml`. """ +import argparse import dataclasses import json import logging import os import re +import subprocess import typing from pathlib import Path from typing import List, Dict, Any, Optional @@ -25,13 +27,19 @@ JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" Job = Dict[str, Any] -def name_jobs(jobs: List[Dict], prefix: str) -> List[Job]: +def add_job_properties(jobs: List[Dict], prefix: str) -> List[Job]: """ - Add a `name` attribute to each job, based on its image and the given `prefix`. + Modify the `name` attribute of each job, based on its base name and the given `prefix`. + Add an `image` attribute to each job, based on its image. """ + modified_jobs = [] for job in jobs: - job["name"] = f"{prefix} - {job['image']}" - return jobs + # Create a copy of the `job` dictionary to avoid modifying `jobs` + new_job = dict(job) + new_job["image"] = get_job_image(new_job) + new_job["full_name"] = f"{prefix} - {new_job['name']}" + modified_jobs.append(new_job) + return modified_jobs def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: @@ -39,11 +47,15 @@ def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: Prepends `environment` to the `env` attribute of each job. The `env` of each job has higher precedence than `environment`. """ + modified_jobs = [] for job in jobs: env = environment.copy() env.update(job.get("env", {})) - job["env"] = env - return jobs + + new_job = dict(job) + new_job["env"] = env + modified_jobs.append(new_job) + return modified_jobs @dataclasses.dataclass @@ -116,7 +128,9 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: if isinstance(run_type, PRRunType): - return add_base_env(name_jobs(job_data["pr"], "PR"), job_data["envs"]["pr"]) + return add_base_env( + add_job_properties(job_data["pr"], "PR"), job_data["envs"]["pr"] + ) elif isinstance(run_type, TryRunType): jobs = job_data["try"] custom_jobs = run_type.custom_jobs @@ -130,7 +144,7 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ jobs = [] unknown_jobs = [] for custom_job in custom_jobs: - job = [j for j in job_data["auto"] if j["image"] == custom_job] + job = [j for j in job_data["auto"] if j["name"] == custom_job] if not job: unknown_jobs.append(custom_job) continue @@ -140,10 +154,10 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ f"Custom job(s) `{unknown_jobs}` not found in auto jobs" ) - return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"]) + return add_base_env(add_job_properties(jobs, "try"), job_data["envs"]["try"]) elif isinstance(run_type, AutoRunType): return add_base_env( - name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"] + add_job_properties(job_data["auto"], "auto"), job_data["envs"]["auto"] ) return [] @@ -181,12 +195,64 @@ def format_run_type(run_type: WorkflowRunType) -> str: raise AssertionError() -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) +def get_job_image(job: Job) -> str: + """ + By default, the Docker image of a job is based on its name. + However, it can be overridden by its IMAGE environment variable. + """ + env = job.get("env", {}) + # Return the IMAGE environment variable if it exists, otherwise return the job name + return env.get("IMAGE", job["name"]) - with open(JOBS_YAML_PATH) as f: - data = yaml.safe_load(f) +def is_linux_job(job: Job) -> bool: + return "ubuntu" in job["os"] + + +def find_linux_job(job_data: Dict[str, Any], job_name: str, pr_jobs: bool) -> Job: + candidates = job_data["pr"] if pr_jobs else job_data["auto"] + jobs = [job for job in candidates if job.get("name") == job_name] + if len(jobs) == 0: + available_jobs = "\n".join( + sorted(job["name"] for job in candidates if is_linux_job(job)) + ) + raise Exception(f"""Job `{job_name}` not found in {'pr' if pr_jobs else 'auto'} jobs. +The following jobs are available: +{available_jobs}""") + assert len(jobs) == 1 + + job = jobs[0] + if not is_linux_job(job): + raise Exception("Only Linux jobs can be executed locally") + return job + + +def run_workflow_locally(job_data: Dict[str, Any], job_name: str, pr_jobs: bool): + DOCKER_DIR = Path(__file__).absolute().parent.parent / "docker" + + job = find_linux_job(job_data, job_name=job_name, pr_jobs=pr_jobs) + + custom_env = {} + # Replicate src/ci/scripts/setup-environment.sh + # Adds custom environment variables to the job + if job_name.startswith("dist-"): + if job_name.endswith("-alt"): + custom_env["DEPLOY_ALT"] = "1" + else: + custom_env["DEPLOY"] = "1" + custom_env.update({k: str(v) for (k, v) in job.get("env", {}).items()}) + + args = [str(DOCKER_DIR / "run.sh"), get_job_image(job)] + env_formatted = [f"{k}={v}" for (k, v) in sorted(custom_env.items())] + print(f"Executing `{' '.join(env_formatted)} {' '.join(args)}`") + + env = os.environ.copy() + env.update(custom_env) + + subprocess.run(args, env=env) + + +def calculate_job_matrix(job_data: Dict[str, Any]): github_ctx = get_github_ctx() run_type = find_run_type(github_ctx) @@ -197,7 +263,7 @@ if __name__ == "__main__": jobs = [] if run_type is not None: - jobs = calculate_jobs(run_type, data) + jobs = calculate_jobs(run_type, job_data) jobs = skip_jobs(jobs, channel) if not jobs: @@ -208,3 +274,45 @@ if __name__ == "__main__": logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") print(f"jobs={json.dumps(jobs)}") print(f"run_type={run_type}") + + +def create_cli_parser(): + parser = argparse.ArgumentParser( + prog="ci.py", description="Generate or run CI workflows" + ) + subparsers = parser.add_subparsers( + help="Command to execute", dest="command", required=True + ) + subparsers.add_parser( + "calculate-job-matrix", + help="Generate a matrix of jobs that should be executed in CI", + ) + run_parser = subparsers.add_parser( + "run-local", help="Run a CI jobs locally (on Linux)" + ) + run_parser.add_argument( + "job_name", + help="CI job that should be executed. By default, a merge (auto) " + "job with the given name will be executed", + ) + run_parser.add_argument( + "--pr", action="store_true", help="Run a PR job instead of an auto job" + ) + return parser + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + with open(JOBS_YAML_PATH) as f: + data = yaml.safe_load(f) + + parser = create_cli_parser() + args = parser.parse_args() + + if args.command == "calculate-job-matrix": + calculate_job_matrix(data) + elif args.command == "run-local": + run_workflow_locally(data, args.job_name, args.pr) + else: + raise Exception(f"Unknown command {args.command}") diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 876a7793592..6bf4a75d080 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -91,26 +91,26 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: mingw-check-tidy + - name: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - image: x86_64-gnu-llvm-18 + - name: x86_64-gnu-llvm-18 env: ENABLE_GCC_CODEGEN: "1" # We are adding (temporarily) a dummy commit on the compiler READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools <<: *job-linux-16c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c @@ -123,106 +123,106 @@ auto: # Linux/Docker builders # ############################# - - image: aarch64-gnu + - name: aarch64-gnu <<: *job-aarch64-linux - - image: aarch64-gnu-debug + - name: aarch64-gnu-debug <<: *job-aarch64-linux - - image: arm-android + - name: arm-android <<: *job-linux-4c - - image: armhf-gnu + - name: armhf-gnu <<: *job-linux-4c - - image: dist-aarch64-linux + - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-4c + <<: *job-aarch64-linux - - image: dist-android + - name: dist-android <<: *job-linux-4c - - image: dist-arm-linux + - name: dist-arm-linux <<: *job-linux-8c - - image: dist-armhf-linux + - name: dist-armhf-linux <<: *job-linux-4c - - image: dist-armv7-linux + - name: dist-armv7-linux <<: *job-linux-4c - - image: dist-i586-gnu-i586-i686-musl + - name: dist-i586-gnu-i586-i686-musl <<: *job-linux-4c - - image: dist-i686-linux + - name: dist-i686-linux <<: *job-linux-4c - - image: dist-loongarch64-linux + - name: dist-loongarch64-linux <<: *job-linux-4c - - image: dist-loongarch64-musl + - name: dist-loongarch64-musl <<: *job-linux-4c - - image: dist-ohos + - name: dist-ohos <<: *job-linux-4c - - image: dist-powerpc-linux + - name: dist-powerpc-linux <<: *job-linux-4c - - image: dist-powerpc64-linux + - name: dist-powerpc64-linux <<: *job-linux-4c - - image: dist-powerpc64le-linux + - name: dist-powerpc64le-linux <<: *job-linux-4c-largedisk - - image: dist-riscv64-linux + - name: dist-riscv64-linux <<: *job-linux-4c - - image: dist-s390x-linux + - name: dist-s390x-linux <<: *job-linux-4c - - image: dist-various-1 + - name: dist-various-1 <<: *job-linux-4c - - image: dist-various-2 + - name: dist-various-2 <<: *job-linux-4c - - image: dist-x86_64-freebsd + - name: dist-x86_64-freebsd <<: *job-linux-4c - - image: dist-x86_64-illumos + - name: dist-x86_64-illumos <<: *job-linux-4c - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-linux-alt + - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-musl + - name: dist-x86_64-musl env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-4c - - image: dist-x86_64-netbsd + - name: dist-x86_64-netbsd <<: *job-linux-4c # The i686-gnu job is split into multiple jobs to run tests in parallel. # i686-gnu-1 skips tests that run in i686-gnu-2. - - image: i686-gnu-1 + - name: i686-gnu-1 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-1 - - image: i686-gnu-2 + - name: i686-gnu-2 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set2.sh @@ -230,14 +230,14 @@ auto: # The i686-gnu-nopt job is split into multiple jobs to run tests in parallel. # i686-gnu-nopt-1 skips tests that run in i686-gnu-nopt-2 - - image: i686-gnu-nopt-1 + - name: i686-gnu-nopt-1 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-nopt-1 - - image: i686-gnu-nopt-2 + - name: i686-gnu-nopt-2 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: >- @@ -245,13 +245,13 @@ auto: /scripts/stage_2_test_set2.sh <<: *job-linux-4c - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: test-various + - name: test-various <<: *job-linux-4c - - image: x86_64-fuchsia + - name: x86_64-fuchsia # Only run this job on the nightly channel. Fuchsia requires # nightly features to compile, and this job would fail if # executed on beta and stable. @@ -260,10 +260,10 @@ auto: # Tests integration with Rust for Linux. # Builds stage 1 compiler and tries to compile a few RfL examples with it. - - image: x86_64-rust-for-linux + - name: x86_64-rust-for-linux <<: *job-linux-4c - - image: x86_64-gnu + - name: x86_64-gnu <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full @@ -271,7 +271,7 @@ auto: # depend on the channel being built (for example if they include the # channel name on the output), and this builder prevents landing # changes that would result in broken builds after a promotion. - - image: x86_64-gnu-stable + - name: x86_64-gnu-stable # Only run this job on the nightly channel. Running this on beta # could cause failures when `dev: 1` in `stage0.txt`, and running # this on stable is useless. @@ -281,20 +281,20 @@ auto: RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable <<: *job-linux-4c - - image: x86_64-gnu-aux + - name: x86_64-gnu-aux <<: *job-linux-4c - - image: x86_64-gnu-debug + - name: x86_64-gnu-debug # This seems to be needed because a full stage 2 build + run-make tests # overwhelms the storage capacity of the standard 4c runner. <<: *job-linux-4c-largedisk - - image: x86_64-gnu-distcheck + - name: x86_64-gnu-distcheck <<: *job-linux-8c # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - image: x86_64-gnu-llvm-19-1 + - name: x86_64-gnu-llvm-19-1 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -302,7 +302,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - image: x86_64-gnu-llvm-19-2 + - name: x86_64-gnu-llvm-19-2 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -310,7 +310,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - image: x86_64-gnu-llvm-19-3 + - name: x86_64-gnu-llvm-19-3 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -319,7 +319,7 @@ auto: # The x86_64-gnu-llvm-18 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-18-1 skips tests that run in x86_64-gnu-llvm-18-{2,3}. - - image: x86_64-gnu-llvm-18-1 + - name: x86_64-gnu-llvm-18-1 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -328,7 +328,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,3} - - image: x86_64-gnu-llvm-18-2 + - name: x86_64-gnu-llvm-18-2 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -337,7 +337,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,2} - - image: x86_64-gnu-llvm-18-3 + - name: x86_64-gnu-llvm-18-3 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -345,10 +345,10 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - - image: x86_64-gnu-nopt + - name: x86_64-gnu-nopt <<: *job-linux-4c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools env: DEPLOY_TOOLSTATES_JSON: toolstates-linux.json <<: *job-linux-4c @@ -357,7 +357,7 @@ auto: # macOS Builders # #################### - - image: dist-x86_64-apple + - name: dist-x86_64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 @@ -371,7 +371,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-xl - - image: dist-apple-various + - name: dist-apple-various env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi # Mac Catalyst cannot currently compile the sanitizer: @@ -385,19 +385,19 @@ auto: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - image: x86_64-apple-1 + - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests <<: *job-macos-xl - - image: x86_64-apple-2 + - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests <<: *job-macos-xl # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: dist-aarch64-apple + - name: dist-aarch64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -421,7 +421,7 @@ auto: <<: *job-macos-m1 # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: aarch64-apple + - name: aarch64-apple env: SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -442,20 +442,20 @@ auto: # Windows Builders # ###################### - - image: x86_64-msvc + - name: x86_64-msvc env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler SCRIPT: make ci-msvc <<: *job-windows-8c - - image: i686-msvc + - name: i686-msvc env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-msvc <<: *job-windows-8c # x86_64-msvc-ext is split into multiple jobs to run tests in parallel. - - image: x86_64-msvc-ext1 + - name: x86_64-msvc-ext1 env: SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld @@ -464,7 +464,7 @@ auto: # Temporary builder to workaround CI issues # See <https://github.com/rust-lang/rust/issues/127883> #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - - image: x86_64-msvc-ext2 + - name: x86_64-msvc-ext2 env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && @@ -476,7 +476,7 @@ auto: <<: *job-windows # Run `checktools.sh` and upload the toolstate file. - - image: x86_64-msvc-ext3 + - name: x86_64-msvc-ext3 env: SCRIPT: src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows HOST_TARGET: x86_64-pc-windows-msvc @@ -500,7 +500,7 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - - image: i686-mingw + - name: i686-mingw env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw @@ -510,7 +510,7 @@ auto: <<: *job-windows-8c # x86_64-mingw is split into two jobs to run tests in parallel. - - image: x86_64-mingw-1 + - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-x RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -519,7 +519,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: x86_64-mingw-2 + - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-bootstrap RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -528,7 +528,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: dist-x86_64-msvc + - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -542,7 +542,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows-8c - - image: dist-i686-msvc + - name: dist-i686-msvc env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc @@ -555,7 +555,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-aarch64-msvc + - name: dist-aarch64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -567,7 +567,7 @@ auto: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows - - image: dist-i686-mingw + - name: dist-i686-mingw env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-gnu @@ -580,7 +580,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-mingw + - name: dist-x86_64-mingw env: SCRIPT: python x.py dist bootstrap --include-default-paths RUST_CONFIGURE_ARGS: >- @@ -593,7 +593,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-msvc-alt + - name: dist-x86_64-msvc-alt env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 2a686b5471c..7ce7d4ddf3e 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -70,46 +70,21 @@ $ ENABLE_LINKCHECK=1 mdbook serve We use `mdbook-toc` to auto-generate TOCs for long sections. You can invoke the preprocessor by including the `<!-- toc -->` marker at the place where you want the TOC. -## How to fix toolstate failures - -> [!NOTE] -> Currently, we do not track the rustc-dev-guide toolstate due to -> [spurious failures](https://github.com/rust-lang/rust/pull/71731), -> but we leave these instructions for when we do it again in the future. - -1. You will get a ping from the toolstate commit. e.g. https://github.com/rust-lang-nursery/rust-toolstate/commit/8ffa0e4c30ac9ba8546b7046e5c4ccc2b96ebdd4 - -2. The commit contains a link to the PR that caused the breakage. e.g. https://github.com/rust-lang/rust/pull/64321 - -3. If you go to that PR's thread, there is a post from bors with a link to the CI status: https://github.com/rust-lang/rust/pull/64321#issuecomment-529763807 - -4. Follow the check-actions link to get to the Actions page for that build - -5. There will be approximately 1 billion different jobs for the build. They are for different configurations and platforms. The rustc-dev-guide build only runs on the Linux x86_64-gnu-tools job. So click on that job in the list, which is about 60% down in the list. - -6. Click the Run build step in the job to get the console log for the step. - -7. Click on the log and Ctrl-f to get a search box in the log - -8. Search for rustc-dev-guide. This gets you to the place where the links are checked. It is usually ~11K lines into the log. - -9. Look at the links in the log near that point in the log - -10. Fix those links in the rustc-dev-guide (by making a PR in the rustc-dev-guide repo) - -11. Make a PR on the rust-lang/rust repo to update the rustc-dev-guide git submodule in src/docs/rustc-dev-guide. -To make a PR, the following steps are useful. - -```bash -# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory -git submodule update --remote src/doc/rustc-dev-guide -git add -u -git commit -m "Update rustc-dev-guide" -# Note that you can use -i, which is short for --incremental, in the following command -./x test --incremental src/doc/rustc-dev-guide # This is optional and should succeed anyway -# Open a PR in rust-lang/rust -``` - -12. Wait for PR to merge - -Voilà ! +## Synchronizing josh subtree with rustc + +This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions. + +### Pull changes from `rust-lang/rust` into this repository +1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` +2) Run the pull command + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-pull + ``` +3) Push the branch to your fork and create a PR into `rustc-dev-guide` + +### Push changes from this repository into `rust-lang/rust` +1) Run the push command to create a branch named `<branch-name>` in a `rustc` fork under the `<gh-username>` account + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-push <branch-name> <gh-username> + ``` +2) Create a PR from `<branch-name>` into `rust-lang/rust` diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock new file mode 100644 index 00000000000..844518628c4 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock @@ -0,0 +1,430 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "josh-sync" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "directories", + "xshell", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xshell" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml new file mode 100644 index 00000000000..81d0d1ebd22 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "josh-sync" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.95" +clap = { version = "4.5.21", features = ["derive"] } +directories = "5" +xshell = "0.2.6" diff --git a/src/doc/rustc-dev-guide/josh-sync/README.md b/src/doc/rustc-dev-guide/josh-sync/README.md new file mode 100644 index 00000000000..a3dd876e8b8 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/README.md @@ -0,0 +1,4 @@ +# Git josh sync +This utility serves for syncing the josh git subtree to and from the rust-lang/rust repository. + +See CLI help for usage. diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs new file mode 100644 index 00000000000..84613ad8689 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/main.rs @@ -0,0 +1,32 @@ +use clap::Parser; +use crate::sync::GitSync; + +mod sync; + +#[derive(clap::Parser)] +enum Args { + /// Pull changes from the main `rustc` repository. + /// This creates new commits that should be then merged into `rustc-dev-guide`. + RustcPull, + /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given + /// GitHub `username`. + /// The pushed branch should then be merged into the `rustc` repository. + RustcPush { + branch: String, + github_username: String + } +} + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + let sync = GitSync::from_current_dir()?; + match args { + Args::RustcPull => { + sync.rustc_pull(None)?; + } + Args::RustcPush { github_username, branch } => { + sync.rustc_push(github_username, branch)?; + } + } + Ok(()) +} diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs new file mode 100644 index 00000000000..da21a4c9a27 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -0,0 +1,234 @@ +use std::ops::Not; +use std::path::PathBuf; +use std::{env, net, process}; +use std::io::Write; +use std::time::Duration; +use anyhow::{anyhow, bail, Context}; +use xshell::{cmd, Shell}; + +/// Used for rustc syncs. +const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide"; +const JOSH_PORT: u16 = 42042; +const UPSTREAM_REPO: &str = "rust-lang/rust"; + +pub struct GitSync { + dir: PathBuf, +} + +/// This code was adapted from the miri repository +/// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236). +impl GitSync { + pub fn from_current_dir() -> anyhow::Result<Self> { + Ok(Self { + dir: std::env::current_dir()? + }) + } + + pub fn rustc_pull(&self, commit: Option<String>) -> anyhow::Result<()> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let commit = commit.map(Ok).unwrap_or_else(|| { + let rust_repo_head = + cmd!(sh, "git ls-remote https://github.com/{UPSTREAM_REPO}/ HEAD").read()?; + rust_repo_head + .split_whitespace() + .next() + .map(|front| front.trim().to_owned()) + .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) + })?; + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + bail!("working directory must be clean before performing rustc pull"); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git"); + + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running git hooks. + // We do this before the merge so that if there are merge conflicts, we have + // the right rust-version file while resolving them. + sh.write_file("rust-version", format!("{commit}\n"))?; + const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; + cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") + .run() + .context("FAILED to commit rust-version file, something went wrong")?; + + // Fetch given rustc commit. + cmd!(sh, "git fetch {josh_url}") + .run() + .inspect_err(|_| { + // Try to un-do the previous `git commit`, to leave the repo in the state we found it. + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); + }) + .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> anyhow::Result<u32> { + Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") + .read() + .context("failed to determine the number of root commits")? + .parse::<u32>()?) + }; + let num_roots_before = num_roots()?; + + // Merge the fetched commit. + const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; + cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") + .run() + .context("FAILED to merge new commits, something went wrong")?; + + // Check that the number of roots did not increase. + if num_roots()? != num_roots_before { + bail!("Josh created a new root commit. This is probably not the history you want."); + } + + drop(josh); + Ok(()) + } + + pub fn rustc_push(&self, github_user: String, branch: String) -> anyhow::Result<()> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let base = sh.read_file("rust-version")?.trim().to_owned(); + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + bail!("working directory must be clean before running `rustc-push`"); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); + + // Find a repo we can do our preparation in. + if let Ok(rustc_git) = env::var("RUSTC_GIT") { + // If rustc_git is `Some`, we'll use an existing fork for the branch updates. + sh.change_dir(rustc_git); + } else { + // Otherwise, do this in the local repo. + println!( + "This will pull a copy of the rust-lang/rust history into this checkout, growing it by about 1GB." + ); + print!( + "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " + ); + std::io::stdout().flush()?; + let mut answer = String::new(); + std::io::stdin().read_line(&mut answer)?; + if answer.trim().to_lowercase() != "y" { + std::process::exit(1); + } + }; + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {github_user}/rust (base: {base})..."); + if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") + .ignore_stderr() + .read() + .is_ok() + { + println!( + "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." + ); + std::process::exit(1); + } + cmd!(sh, "git fetch https://github.com/{UPSTREAM_REPO} {base}").run()?; + cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message + .run()?; + println!(); + + // Do the actual push. + sh.change_dir(&self.dir); + println!("Pushing changes..."); + cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; + println!(); + + // Do a round-trip check to make sure the push worked as expected. + cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; + let head = cmd!(sh, "git rev-parse HEAD").read()?; + let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; + if head != fetch_head { + bail!( + "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ + Expected {head}, got {fetch_head}." + ); + } + println!( + "Confirmed that the push round-trips back to rustc-dev-guide properly. Please create a rustc PR:" + ); + println!( + // Open PR with `subtree update` title to silence the `no-merges` triagebot check + " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=Rustc+dev+guide+subtree+update&body=r?+@ghost" + ); + + drop(josh); + Ok(()) + } + + fn start_josh() -> anyhow::Result<impl Drop> { + // Determine cache directory. + let local_dir = { + let user_dirs = + directories::ProjectDirs::from("org", "rust-lang", "rustc-dev-guide-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = process::Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT.to_string()); + cmd.arg("--no-background"); + cmd.stdout(process::Stdio::null()); + cmd.stderr(process::Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + + // Create a wrapper that stops it on drop. + struct Josh(process::Child); + impl Drop for Josh { + fn drop(&mut self) { + #[cfg(unix)] + { + // Try to gracefully shut it down. + process::Command::new("kill") + .args(["-s", "INT", &self.0.id().to_string()]) + .output() + .expect("failed to SIGINT josh-proxy"); + // Sadly there is no "wait with timeout"... so we just give it some time to finish. + std::thread::sleep(Duration::from_millis(100)); + // Now hopefully it is gone. + if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { + return; + } + } + // If that didn't work (or we're not on Unix), kill it hard. + eprintln!( + "I have to kill josh-proxy the hard way, let's hope this does not break anything." + ); + self.0.kill().expect("failed to SIGKILL josh-proxy"); + } + } + + // Wait until the port is open. We try every 10ms until 1s passed. + for _ in 0..100 { + // This will generally fail immediately when the port is still closed. + let josh_ready = net::TcpStream::connect_timeout( + &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), + Duration::from_millis(1), + ); + if josh_ready.is_ok() { + return Ok(Josh(josh)); + } + // Not ready yet. + std::thread::sleep(Duration::from_millis(10)); + } + bail!("Even after waiting for 1s, josh-proxy is still not available.") + } +} diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version new file mode 100644 index 00000000000..876c32dcf43 --- /dev/null +++ b/src/doc/rustc-dev-guide/rust-version @@ -0,0 +1 @@ +dcfa38fe234de9304169afc6638e81d0dd222c06 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 2fbbb187d6b..a8fddbc7562 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -134,14 +134,13 @@ - [Prologue](./part-4-intro.md) - [Generic parameter definitions](./generic_parameters_summary.md) - - [Implementation nuances of early/late bound parameters](./early-late-bound-params/early-late-bound-implementation-nuances.md) - - [Interactions with turbofishing](./early-late-bound-params/turbofishing-and-early-late-bound.md) + - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) +- [Binders and Higher ranked regions](./ty_module/binders.md) + - [Instantiating binders](./ty_module/instantiating_binders.md) +- [Early vs Late bound parameters](./early_late_parameters.md) - [The `ty` module: representing types](./ty.md) - [ADTs and Generic Arguments](./ty_module/generic_arguments.md) - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md) - - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) - - [`Binder` and Higher ranked regions](./ty_module/binders.md) - - [Instantiating binders](./ty_module/instantiating_binders.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) - [Parameter Environments](./param_env/param_env_summary.md) - [What is it?](./param_env/param_env_what_is_it.md) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 275a1777a99..805291017c2 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -56,7 +56,7 @@ These tools include: By default, the Rust build system does not check for changes to the LLVM source code or its build configuration settings. So, if you need to rebuild the LLVM that is linked -into `rustc`, first delete the file `llvm-finished-building`, which should be located +into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build/<host-triple>/llvm/`. The default rustc compilation pipeline has multiple codegen units, which is diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 3080dc6bf5d..8feda59829b 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -126,4 +126,4 @@ Here is an example of how can `opt-dist` be used locally (outside of CI): [`Environment`]: https://github.com/rust-lang/rust/blob/ee451f8faccf3050c76cdcd82543c917b40c7962/src/tools/opt-dist/src/environment.rs#L5 > Note: if you want to run the actual CI pipeline, instead of running `opt-dist` locally, -> you can execute `DEPLOY=1 src/ci/docker/run.sh dist-x86_64-linux`. +> you can execute `python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux`. diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md deleted file mode 100644 index 01569e8dc6a..00000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md +++ /dev/null @@ -1,197 +0,0 @@ -# Early and Late Bound Parameter Implementation Nuances - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -Understanding this page likely requires a rudimentary understanding of higher ranked -trait bounds/`for<'a>`and also what types such as `dyn for<'a> Trait<'a>` and - `for<'a> fn(&'a u32)` mean. Reading [the nomincon chapter](https://doc.rust-lang.org/nomicon/hrtb.html) -on HRTB may be useful for understanding this syntax. The meaning of `for<'a> fn(&'a u32)` -is incredibly similar to the meaning of `T: for<'a> Trait<'a>`. - -## What does it mean for parameters to be early or late bound - -All function definitions conceptually have a ZST (this is represented by `TyKind::FnDef` in rustc). -The only generics on this ZST are the early bound parameters of the function definition. e.g. -```rust -fn foo<'a>(_: &'a u32) {} - -fn main() { - let b = foo; - // ^ `b` has type `FnDef(foo, [])` (no args because `'a` is late bound) - assert!(std::mem::size_of_val(&b) == 0); -} -``` - -In order to call `b` the late bound parameters do need to be provided, these are inferred at the -call site instead of when we refer to `foo`. -```rust -fn main() { - let b = foo; - let a: &'static u32 = &10; - foo(a); - // the lifetime argument for `'a` on `foo` is inferred at the callsite - // the generic parameter `'a` on `foo` is inferred to `'static` here -} -``` - -Because late bound parameters are not part of the `FnDef`'s args this allows us to prove trait -bounds such as `F: for<'a> Fn(&'a u32)` where `F` is `foo`'s `FnDef`. e.g. -```rust -fn foo_early<'a, T: Trait<'a>>(_: &'a u32, _: T) {} -fn foo_late<'a, T>(_: &'a u32, _: T) {} - -fn accepts_hr_func<F: for<'a> Fn(&'a u32, u32)>(_: F) {} - -fn main() { - // doesn't work, the instantiated bound is `for<'a> FnDef<'?0>: Fn(&'a u32, u32)` - // `foo_early` only implements `for<'a> FnDef<'a>: Fn(&'a u32, u32)`- the lifetime - // of the borrow in the function argument must be the same as the lifetime - // on the `FnDef`. - accepts_hr_func(foo_early); - - // works, the instantiated bound is `for<'a> FnDef: Fn(&'a u32, u32)` - accepts_hr_func(foo_late); -} - -// the builtin `Fn` impls for `foo_early` and `foo_late` look something like: -// `foo_early` -impl<'a, T: Trait<'a>> Fn(&'a u32, T) for FooEarlyFnDef<'a, T> { ... } -// `foo_late` -impl<'a, T> Fn(&'a u32, T) for FooLateFnDef<T> { ... } - -``` - -Early bound parameters are present on the `FnDef`. Late bound generic parameters are not present -on the `FnDef` but are instead constrained by the builtin `Fn*` impl. - -The same distinction applies to closures. Instead of `FnDef` we are talking about the anonymous -closure type. Closures are [currently unsound](https://github.com/rust-lang/rust/issues/84366) in -ways that are closely related to the distinction between early/late bound -parameters (more on this later) - -The early/late boundness of generic parameters is only relevant for the desugaring of -functions/closures into types with builtin `Fn*` impls. It does not make sense to talk about -in other contexts. - -The `generics_of` query in rustc only contains early bound parameters. In this way it acts more -like `generics_of(my_func)` is the generics for the FnDef than the generics provided to the function -body although it's not clear to the author of this section if this was the actual justification for -making `generics_of` behave this way. - -## What parameters are currently late bound - -Below are the current requirements for determining if a generic parameter is late bound. It is worth -keeping in mind that these are not necessarily set in stone and it is almost certainly possible to -be more flexible. - -### Must be a lifetime parameter - -Rust can't support types such as `for<T> dyn Trait<T>` or `for<T> fn(T)`, this is a -fundamental limitation of the language as we are required to monomorphize type/const -parameters and cannot do so behind dynamic dispatch. (technically we could probably -support `for<T> dyn MarkerTrait<T>` as there is nothing to monomorphize) - -Not being able to support `for<T> dyn Trait<T>` resulted in making all type and const -parameters early bound. Only lifetime parameters can be late bound. - -### Must not appear in the where clauses - -In order for a generic parameter to be late bound it must not appear in any where clauses. -This is currently an incredibly simplistic check that causes lifetimes to be early bound even -if the where clause they appear in are always true, or implied by well formedness of function -arguments. e.g. -```rust -fn foo1<'a: 'a>(_: &'a u32) {} -// ^^ early bound parameter because it's in a `'a: 'a` clause -// even though the bound obviously holds all the time -fn foo2<'a, T: Trait<'a>(a: T, b: &'a u32) {} -// ^^ early bound parameter because it's used in the `T: Trait<'a>` clause -fn foo3<'a, T: 'a>(_: &'a T) {} -// ^^ early bound parameter because it's used in the `T: 'a` clause -// even though that bound is implied by wellformedness of `&'a T` -fn foo4<'a, 'b: 'a>(_: Inv<&'a ()>, _: Inv<&'b ()>) {} -// ^^ ^^ ^^^ note: -// ^^ ^^ `Inv` stands for `Invariant` and is used to -// ^^ ^^ make the type parameter invariant. This -// ^^ ^^ is necessary for demonstration purposes as -// ^^ ^^ `for<'a, 'b> fn(&'a (), &'b ())` and -// ^^ ^^ `for<'a> fn(&'a u32, &'a u32)` are subtypes- -// ^^ ^^ of each other which makes the bound trivially -// ^^ ^^ satisfiable when making the fnptr. `Inv` -// ^^ ^^ disables this subtyping. -// ^^ ^^ -// ^^^^^^ both early bound parameters because they are present in the -// `'b: 'a` clause -``` - -The reason for this requirement is that we cannot represent the `T: Trait<'a>` or `'a: 'b` clauses -on a function pointer. `for<'a, 'b> fn(Inv<&'a ()>, Inv<&'b ()>)` is not a valid function pointer to -represent`foo4` as it would allow calling the function without `'b: 'a` holding. - -### Must be constrained by where clauses or function argument types - -The builtin impls of the `Fn*` traits for closures and `FnDef`s cannot not have any unconstrained -parameters. For example the following impl is illegal: -```rust -impl<'a> Trait for u32 { type Assoc = &'a u32; } -``` -We must not end up with a similar impl for the `Fn*` traits e.g. -```rust -impl<'a> Fn<()> for FnDef { type Assoc = &'a u32 } -``` - -Violating this rule can trivially lead to unsoundness as seen in [#84366](https://github.com/rust-lang/rust/issues/84366). -Additionally if we ever support late bound type params then an impl like: -```rust -impl<T> Fn<()> for FnDef { type Assoc = T; } -``` -would break the compiler in various ways. - -In order to ensure that everything functions correctly, we do not allow generic parameters to -be late bound if it would result in a builtin impl that does not constrain all of the generic -parameters on the builtin impl. Making a generic parameter be early bound trivially makes it be -constrained by the builtin impl as it ends up on the self type. - -Because of the requirement that late bound parameters must not appear in where clauses, checking -this is simpler than the rules for checking impl headers constrain all the parameters on the impl. -We only have to ensure that all late bound parameters appear at least once in the function argument -types outside of an alias (e.g. an associated type). - -The requirement that they not indirectly be in the args of an alias for it to count is the -same as why the follow code is forbidden: -```rust -impl<T: Trait> OtherTrait for <T as Trait>::Assoc { type Assoc = T } -``` -There is no guarantee that `<T as Trait>::Assoc` will normalize to different types for every -instantiation of `T`. If we were to allow this impl we could get overlapping impls and the -same is true of the builtin `Fn*` impls. - -## Making more generic parameters late bound - -It is generally considered desirable for more parameters to be late bound as it makes -the builtin `Fn*` impls more flexible. Right now many of the requirements for making -a parameter late bound are overly restrictive as they are tied to what we can currently -(or can ever) do with fn ptrs. - -It would be theoretically possible to support late bound params in `where`-clauses in the -language by introducing implication types which would allow us to express types such as: -`for<'a, 'b: 'a> fn(Inv<&'a u32>, Inv<&'b u32>)` which would ensure `'b: 'a` is upheld when -calling the function pointer. - -It would also be theoretically possible to support it by making the coercion to a fn ptr -instantiate the parameter with an infer var while still allowing the FnDef to not have the -generic parameter present as trait impls are perfectly capable of representing the where clauses -on the function on the impl itself. This would also allow us to support late bound type/const -vars allowing bounds like `F: for<T> Fn(T)` to hold. - -It is almost somewhat unclear if we can change the `Fn` traits to be structured differently -so that we never have to make a parameter early bound just to make the builtin impl have all -generics be constrained. Of all the possible causes of a generic parameter being early bound -this seems the most difficult to remove. - -Whether these would be good ideas to implement is a separate question- they are only brought -up to illustrate that the current rules are not necessarily set in stone and a result of -"its the only way of doing this". - diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md deleted file mode 100644 index 6fc30c6767d..00000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md +++ /dev/null @@ -1,125 +0,0 @@ -# Turbofishing's interactions with early/late bound parameters - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -The early/late bound parameter distinction on functions introduces some complications -when providing generic arguments to functions. This document discusses what those are -and how they might interact with future changes to make more things late bound. - -## Can't turbofish generic arguments on functions sometimes - -When a function has any late bound lifetime parameters (be they explicitly defined or -implicitly introduced via lifetime elision) we disallow specifying any lifetime arguments -on the function. Sometimes this is a hard error other times it is a future compat lint -([`late_bound_lifetime_arguments`](https://github.com/rust-lang/rust/issues/42868)). - -```rust -fn early<'a: 'a>(a: &'a ()) -> &'a () { a } -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn mixed<'a, 'b: 'b>(a: &'a (), b: &'b ()) -> &'a () { a } - -struct Foo; -impl Foo { - fn late<'a>(self, a: &'a ()) -> &'a () { a } -} - -fn main() { - // fine - let f = early::<'static>; - - // some variation of hard errors and future compat lints - Foo.late::<'static>(&()); - let f = late::<'static>; - let f = mixed::<'static, 'static>; - let f = mixed::<'static>; - late::<'static>(&()); -} -``` - -The justification for this is that late bound parameters are not present on the -`FnDef` so the arguments to late bound parameters can't be present in the generic arguments -for the type. i.e. the `late` function in the above code snippet would not have -any generic parameters on the `FnDef` zst: -```rust -// example desugaring of the `late` function and its zst + builtin Fn impl -struct LateFnDef; -impl<'a> Fn<(&'a ())> for LateFnDef { - type Output = &'a (); - ... -} -``` - -The cause for some situations giving future compat lints and others giving hard errors -is a little arbitrary but explainable: -- It's always a hard error for method calls -- It's only a hard error on paths to free functions if there is no unambiguous way to -create the generic arguments for the fndef from the lifetime arguments. (i.e. the amount of -lifetimes provided must be exactly equal to the amount of early bound lifetimes or -else it's a hard error) - -## Back compat issues from turning early bound to late bound - -Because of the previously mentioned restriction on turbofishing generic arguments, it -is a breaking change to upgrade a lifetime from early bound to late bound as it can cause -existing turbofishies to become hard errors/future compat lints. - -Many t-types members have expressed interest in wanting more parameters to be late bound. -We cannot do so if making something late bound is going to break code that many would -expect to work (judging by the future compat lint issue many people do expect to be able -to turbofish late bound parameters). - -## Interactions with late bound type/const parameters - -If we were to make some type/const parameters late bound we would definitely not want -to disallow turbofishing them as it presumably(?) would break a Tonne of code. - -While lifetimes do differ from type/consts in some ways I(BoxyUwU) do not believe there -is any justification for why it would make sense to allow turbofishing late bound -type/const parameters but not late bound lifetimes. - -## Removing the hard error/fcw - -From reasons above it seems reasonable that we may want to remove the hard error and fcw -(removing the errors/fcw is definitely a blocker for making more things late bound). - -example behaviour: -```rust -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn accepts_fn(_: impl for<'a> Fn(&'a ()) -> &'a ()) {} -fn accepts_fn_2(_: impl Fn(&'static ()) -> &'static ()) {} - -fn main() { - let f = late::<'static>; - - accepts_fn(f); //~ error: `f` doesn't implement `for<'a> Fn(&'a ()) -> &'a ()` - accepts_fn_2(f) // works - - accepts_fn(late) // works -} -```` - -one potential complication is that we would want a way to specify a generic argument -to a function without having to specify arguments for all previous parameters. i.e. -ideally you could write the following code somehow. -```rust -fn late<'a, 'b>(_: &'a (), _: &'b ()) {} - -fn accepts_fn(_: impl for<'a> Fn(&'a (), &'static ())) {} - -fn main() { - // a naive implementation would have an inference variable as - // the argument to the `'a` parameter no longer allowing the `FnDef` - // to satisfy the bound `for<'a> Fn(&'a ())` - let f = late::<'_, 'static>; - accepts_fn(f); -} -``` -Maybe we can just special case HIR ty lowering for `_`/`'_` arguments for late bound -parameters somehow and have it not mean the same thing as `_` for early bound parameters. -Regardless I think we would need a solution that would allow writing the above code even -if it was done by some new syntax such as having to write `late::<k#no_argument, 'static>` -(naturally `k#no_argument` would only make sense as an argument to late bound parameters). diff --git a/src/doc/rustc-dev-guide/src/early_late_parameters.md b/src/doc/rustc-dev-guide/src/early_late_parameters.md new file mode 100644 index 00000000000..6d13655294d --- /dev/null +++ b/src/doc/rustc-dev-guide/src/early_late_parameters.md @@ -0,0 +1,424 @@ + +# Early vs Late bound parameters + +<!-- toc --> + +> **NOTE**: This chapter largely talks about early/late bound as being solely relevant when discussing function item types/function definitions. This is potentially not completely true, async blocks and closures should likely be discussed somewhat in this chapter. + +## What does it mean to be "early" bound or "late" bound + +Every function definition has a corresponding ZST that implements the `Fn*` traits known as a [function item type][function_item_type]. This part of the chapter will talk a little bit about the "desugaring" of function item types as it is useful context for explaining the difference between early bound and late bound generic parameters. + +Let's start with a very trivial example involving no generic parameters: + +```rust +fn foo(a: String) -> u8 { + # 1 + /* snip */ +} +``` + +If we explicitly wrote out the definitions for the function item type corresponding to `foo` and its associated `Fn` impl it would look something like this: +```rust,ignore +struct FooFnItem; + +impl Fn<(String,)> for FooFnItem { + type Output = u8; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The builtin impls for the `FnMut`/`FnOnce` traits as well as the impls for `Copy` and `Clone` were omitted for brevity reasons (although these traits *are* implemented for function item types). + +A slightly more complicated example would involve introducing generic parameters to the function: +```rust +fn foo<T: Sized>(a: T) -> T { + # a + /* snip */ +} +``` +Writing out the definitions would look something like this: +```rust,ignore +struct FooFnItem<T: Sized>(PhantomData<fn(T) -> T>); + +impl<T: Sized> Fn<(T,)> for FooFnItem<T> { + type Output = T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +Note that the function item type `FooFnItem` is generic over some type parameter `T` as defined on the function `foo`. However, not all generic parameters defined on functions are also defined on the function item type as demonstrated here: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} +``` +With its "desugared" form looking like so: +```rust,ignore +struct FooFnItem<T: Sized>(PhantomData<for<'a> fn(&'a T) -> &'a T>); + +impl<'a, T: Sized> Fn<(&'a T,)> for FooFnItem<T> { + type Output = &'a T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The lifetime parameter `'a` from the function `foo` is not present on the function item type `FooFnItem` and is instead introduced on the builtin impl solely for use in representing the argument types. + +Generic parameters not all being defined on the function item type means that there are two steps where generic arguments are provided when calling a function. +1. Naming the function (e.g. `let a = foo;`) the arguments for `FooFnItem` are provided. +2. Calling the function (e.g. `a(&10);`) any parameters defined on the builtin impl are provided. + +This two-step system is where the early vs late naming scheme comes from, early bound parameters are provided in the *earliest* step (naming the function), whereas late bound parameters are provided in the *latest* step (calling the function). + +Looking at the desugaring from the previous example we can tell that `T` is an early bound type parameter and `'a` is a late bound lifetime parameter as `T` is present on the function item type but `'a` is not. See this example of calling `foo` annotated with where each generic parameter has an argument provided: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} + +// Here we provide a type argument `String` to the +// type parameter `T` on the function item type +let my_func = foo::<String>; + +// Here (implicitly) a lifetime argument is provided +// to the lifetime parameter `'a` on the builtin impl. +my_func(&String::new()); +``` + +[function_item_type]: https://doc.rust-lang.org/reference/types/function-item.html + +## Differences between early and late bound parameters + +### Higher ranked function pointers and trait bounds + +A generic parameter being late bound allows for more flexible usage of the function item. For example if we have some function `foo` with an early bound lifetime parameter and some function `bar` with a late bound lifetime parameter `'a` we would have the following builtin `Fn` impls: +```rust,ignore +impl<'a> Fn<(&'a String,)> for FooFnItem<'a> { /* ... */ } +impl<'a> Fn<(&'a String,)> for BarFnItem { /* ... */ } +``` + +The `bar` function has a strictly more flexible signature as the function item type can be called with a borrow with *any* lifetime, whereas the `foo` function item type would only be callable with a borrow with the same lifetime on the function item type. We can show this by simply trying to call `foo`'s function item type multiple times with different lifetimes: + +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +// Early bound generic parameters are instantiated here when naming +// the function `foo`. As `'a` is early bound an argument is provided. +let f = foo::<'_>; + +// Both function arguments are required to have the same lifetime as +// the lifetime parameter being early bound means that `f` is only +// callable for one specific lifetime. +// +// As we call this with borrows of different lifetimes, the borrow checker +// will error here. +f(&String::new()); +f(&String::new()); +``` + +In this example we call `foo`'s function item type twice, each time with a borrow of a temporary. These two borrows could not possible have lifetimes that overlap as the temporaries are only alive during the function call, not after. The lifetime parameter on `foo` being early bound requires all callers of `f` to provide a borrow with the same lifetime, as this is not possible the borrow checker errors. + +If the lifetime parameter on `foo` was late bound this would be able to compile as each caller could provide a different lifetime argument for its borrow. See the following example which demonstrates this using the `bar` function defined above: + +```rust +#fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +#fn bar<'a>(b: &'a String) -> &'a String { b } + +// Early bound parameters are instantiated here, however as `'a` is +// late bound it is not provided here. +let b = bar; + +// Late bound parameters are instantiated separately at each call site +// allowing different lifetimes to be used by each caller. +b(&String::new()); +b(&String::new()); +``` + +This is reflected in the ability to coerce function item types to higher ranked function pointers and prove higher ranked `Fn` trait bounds. We can demonstrate this with the following example: +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +fn accepts_hr_fn(_: impl for<'a> Fn(&'a String) -> &'a String) {} + +fn higher_ranked_trait_bound() { + let bar_fn_item = bar; + accepts_hr_fn(bar_fn_item); + + let foo_fn_item = foo::<'_>; + // errors + accepts_hr_fn(foo_fn_item); +} + +fn higher_ranked_fn_ptr() { + let bar_fn_item = bar; + let fn_ptr: for<'a> fn(&'a String) -> &'a String = bar_fn_item; + + let foo_fn_item = foo::<'_>; + // errors + let fn_ptr: for<'a> fn(&'a String) -> &'a String = foo_fn_item; +} +``` + +In both of these cases the borrow checker errors as it does not consider `foo_fn_item` to be callable with a borrow of any lifetime. This is due to the fact that the lifetime parameter on `foo` is early bound, causing `foo_fn_item` to have a type of `FooFnItem<'_>` which (as demonstrated by the desugared `Fn` impl) is only callable with a borrow of the same lifetime `'_`. + +### Turbofishing in the presence of late bound parameters + +As mentioned previously, the distinction between early and late bound parameters means that there are two places where generic parameters are instantiated: +- When naming a function (early) +- When calling a function (late) + +There currently is no syntax for explicitly specifying generic arguments for late bound parameters as part of the call step, only specifying generic arguments when naming a function. The syntax `foo::<'static>();`, despite being part of a function call, behaves as `(foo::<'static>)();` and instantiates the early bound generic parameters on the function item type. + +See the following example: +```rust +fn foo<'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem<????> */ = foo::<'static>; +``` + +The above example errors as the lifetime parameter `'a` is late bound and so cannot be instantiated as part of the "naming a function" step. If we make the lifetime parameter early bound we will see this code start to compile: +```rust +fn foo<'a: 'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem<'static> */ = foo::<'static>; +``` + +What the current implementation of the compiler aims to do is error when specifying lifetime arguments to a function that has both early *and* late bound lifetime parameters. In practice, due to excessive breakage, some cases are actually only future compatibility warnings ([#42868](https://github.com/rust-lang/rust/issues/42868)): +- When the amount of lifetime arguments is the same as the number of early bound lifetime parameters a FCW is emitted instead of an error +- An error is always downgraded to a FCW when using method call syntax + +To demonstrate this we can write out the different kinds of functions and give them both a late and early bound lifetime: +```rust,ignore +fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} + +struct Foo; + +trait Trait: Sized { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +} + +impl Trait for Foo { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} +``` + +Then, for the first case, we can call each function with a single lifetime argument (corresponding to the one early bound lifetime parameter) and note that it only results in a FCW rather than a hard error. +```rust +#![deny(late_bound_lifetime_arguments)] + +#fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +#struct Foo; +# +#trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +#} +# +#impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +#impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +// Specifying as many arguments as there are early +// bound parameters is always a future compat warning +Foo.trait_method::<'static>(&(), &()); +Foo::trait_method::<'static>(Foo, &(), &()); +Foo::trait_function::<'static>(&(), &()); +Foo.inherent_method::<'static>(&(), &()); +Foo::inherent_function::<'static>(&(), &()); +free_function::<'static>(&(), &()); +``` + +For the second case we call each function with more lifetime arguments than there are lifetime parameters (be it early or late bound) and note that method calls result in a FCW as opposed to the free/associated functions which result in a hard error: +```rust +#fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +#struct Foo; +# +#trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +#} +# +#impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +#impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +// Specifying more arguments than there are early +// bound parameters is a future compat warning when +// using method call syntax. +Foo.trait_method::<'static, 'static, 'static>(&(), &()); +Foo.inherent_method::<'static, 'static, 'static>(&(), &()); +// However, it is a hard error when not using method call syntax. +Foo::trait_method::<'static, 'static, 'static>(Foo, &(), &()); +Foo::trait_function::<'static, 'static, 'static>(&(), &()); +Foo::inherent_function::<'static, 'static, 'static>(&(), &()); +free_function::<'static, 'static, 'static>(&(), &()); +``` + +Even when specifying enough lifetime arguments for both the late and early bound lifetime parameter, these arguments are not actually used to annotate the lifetime provided to late bound parameters. We can demonstrate this by turbofishing `'static` to a function while providing a non-static borrow: +```rust +struct Foo; + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b String ) {} +} + +Foo.inherent_method::<'static, 'static>(&(), &String::new()); +``` + +This compiles even though the `&String::new()` function argument does not have a `'static` lifetime, this is because "extra" lifetime arguments are discarded rather than taken into account for late bound parameters when actually calling the function. + +### Liveness of types with late bound parameters + +When checking type outlives bounds involving function item types we take into account early bound parameters. For example: + +```rust +fn foo<T>(_: T) {} + +fn requires_static<T: 'static>(_: T) {} + +fn bar<T>() { + let f /* : FooFnItem<T> */ = foo::<T>; + requires_static(f); +} +``` + +As the type parameter `T` is early bound, the desugaring of the function item type for `foo` would look something like `struct FooFnItem<T>`. Then in order for `FooFnItem<T>: 'static` to hold we must also require `T: 'static` to hold as otherwise we would wind up with soundness bugs. + +Unfortunately, due to bugs in the compiler, we do not take into account early bound *lifetimes*, which is the cause of the open soundness bug [#84366](https://github.com/rust-lang/rust/issues/84366). This means that it's impossible to demonstrate a "difference" between early/late bound parameters for liveness/type outlives bounds as the only kind of generic parameters that are able to be late bound are lifetimes which are handled incorrectly. + +Regardless, in theory the code example below *should* demonstrate such a difference once [#84366](https://github.com/rust-lang/rust/issues/84366) is fixed: +```rust +fn early_bound<'a: 'a>(_: &'a String) {} +fn late_bound<'a>(_: &'a String) {} + +fn requires_static<T: 'static>(_: T) {} + +fn bar<'b>() { + let e = early_bound::<'b>; + // this *should* error but does not + requires_static(e); + + let l = late_bound; + // this correctly does not error + requires_static(l); +} +``` + +## Requirements for a parameter to be late bound + +### Must be a lifetime parameter + +Type and Const parameters are not able to be late bound as we do not have a way to support types such as `dyn for<T> Fn(Box<T>)` or `for<T> fn(Box<T>)`. Calling such types requires being able to monomorphize the underlying function which is not possible with indirection through dynamic dispatch. + +### Must not be used in a where clause + +Currently when a generic parameter is used in a where clause it must be early bound. For example: +```rust +# trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a String, _: T) {} +``` + +In this example the lifetime parameter `'a` is considered to be early bound as it appears in the where clause `T: Trait<'a>`. This is true even for "trivial" where clauses such as `'a: 'a` or those implied by wellformedness of function arguments, for example: +```rust +fn foo<'a: 'a>(_: &'a String) {} +fn bar<'a, T: 'a>(_: &'a T) {} +``` + +In both of these functions the lifetime parameter `'a` would be considered to be early bound even though the where clauses they are used in arguably do not actually impose any constraints on the caller. + +The reason for this restriction is a combination of two things: +- We cannot prove bounds on late bound parameters until they have been instantiated +- Function pointers and trait objects do not have a way to represent yet to be proven where clauses from the underlying function + +Take the following example: +```rust +trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a T) {} + +let f = foo::<String>; +let f = f as for<'a> fn(&'a String); +f(&String::new()); +``` + +At *some point* during type checking an error should be emitted for this code as `String` does not implement `Trait` for any lifetime. + +If the lifetime `'a` were late bound then this becomes difficult to check. When naming `foo` we do not know what lifetime should be used as part of the `T: Trait<'a>` trait bound as it has not yet been instantiated. When coercing the function item type to a function pointer we have no way of tracking the `String: Trait<'a>` trait bound that must be proven when calling the function. + +If the lifetime `'a` is early bound (which it is in the current implementation in rustc), then the trait bound can be checked when naming the function `foo`. Requiring parameters used in where clauses to be early bound gives a natural place to check where clauses defined on the function. + +Finally, we do not require lifetimes to be early bound if they are used in *implied bounds*, for example: +```rust +fn foo<'a, T>(_: &'a T) {} + +let f = foo; +f(&String::new()); +f(&String::new()); +``` + +This code compiles, demonstrating that the lifetime parameter is late bound, even though `'a` is used in the type `&'a T` which implicitly requires `T: 'a` to hold. Implied bounds can be treated specially as any types introducing implied bounds are in the signature of the function pointer type, which means that when calling the function we know to prove `T: 'a`. + +### Must be constrained by argument types + +It is important that builtin impls on function item types do not wind up with unconstrained generic parameters as this can lead to unsoundness. This is the same kind of restriction as applies to user written impls, for example the following code results in an error: +```rust +trait Trait { + type Assoc; +} + +impl<'a> Trait for u8 { + type Assoc = &'a String; +} +``` + +The analogous example for builtin impls on function items would be the following: +```rust,ignore +fn foo<'a>() -> &'a String { /* ... */ } +``` +If the lifetime parameter `'a` were to be late bound we would wind up with a builtin impl with an unconstrained lifetime, we can manually write out the desugaring for the function item type and its impls with `'a` being late bound to demonstrate this: +```rust,ignore +// NOTE: this is just for demonstration, in practice `'a` is early bound +struct FooFnItem; + +impl<'a> Fn<()> for FooFnItem { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` + +In order to avoid such a situation we consider `'a` to be early bound which causes the lifetime on the impl to be constrained by the self type: +```rust,ignore +struct FooFnItem<'a>(PhantomData<fn() -> &'a String>); + +impl<'a> Fn<()> for FooFnItem<'a> { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 533f7eb5e73..a7ab3d773ac 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -3,24 +3,24 @@ The `rust-lang/rust` git repository depends on several other repos in the `rust-lang` organization. There are three main ways we use dependencies: 1. As a Cargo dependency through crates.io (e.g. `rustc-rayon`) -2. As a git subtree (e.g. `clippy`) +2. As a git (e.g. `clippy`) or a [josh] (e.g. `miri`) subtree 3. As a git submodule (e.g. `cargo`) -As a general rule, use crates.io for libraries that could be useful for others in the ecosystem; use -subtrees for tools that depend on compiler internals and need to be updated if there are breaking -changes; and use submodules for tools that are independent of the compiler. +As a general rule: +- Use crates.io for libraries that could be useful for others in the ecosystem +- Use subtrees for tools that depend on compiler internals and need to be updated if there are breaking +changes +- Use submodules for tools that are independent of the compiler -## External Dependencies (subtree) +## External Dependencies (subtrees) -As a developer to this repository, you don't have to treat the following external projects -differently from other crates that are directly in this repo: +The following external projects are managed using some form of a `subtree`: -* [Clippy](https://github.com/rust-lang/rust-clippy) -* [Miri] +* [clippy](https://github.com/rust-lang/rust-clippy) +* [miri](https://github.com/rust-lang/miri) * [rustfmt](https://github.com/rust-lang/rustfmt) * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) - -[Miri]: https://github.com/rust-lang/miri +* [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) In contrast to `submodule` dependencies (see below for those), the `subtree` dependencies are just regular files and directories which can @@ -29,6 +29,20 @@ to these tools should be filed against the tools directly in their respective upstream repositories. The exception is that when rustc changes are required to implement a new tool feature or test, that should happen in one collective rustc PR. +`subtree` dependencies are currently managed by two distinct approaches: + +* Using `git subtree` + * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) + * `rustfmt` + * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) +* Using the [josh] tool + * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) + * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) + +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a subtree from `git subtree` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). + +Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo. + ### Synchronizing a subtree Periodically the changes made to subtree based dependencies need to be synchronized between this @@ -84,7 +98,6 @@ Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. - ## External Dependencies (submodules) Building Rust will also use external git repositories tracked using [git @@ -111,3 +124,4 @@ the week leading up to the beta cut. [The Rust Reference]: https://github.com/rust-lang/reference/ [toolstate website]: https://rust-lang-nursery.github.io/rust-toolstate/ [Toolstate chapter]: https://forge.rust-lang.org/infra/toolstate.html +[josh]: https://josh-project.github.io/josh/intro.html diff --git a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md index 3403c9f2991..da38ba0455c 100644 --- a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md +++ b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md @@ -25,42 +25,4 @@ Interestingly, `ty::Generics` does not currently contain _every_ generic paramet [ch_representing_types]: ./ty.md [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html [`GenericParamDef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generics/struct.GenericParamDef.html - -# Early vs Late bound parameters - - -```rust -fn foo<'a, T>(b: &'a T) -> &'a T { b } -// ^^ ^early bound -// ^^ -// ^^late bound -``` - -Generally when referring to an item with generic parameters you must specify a list of generic arguments corresponding to the item's generic parameters. In some cases it is permitted to elide these arguments but still, implicitly, a set of arguments are provided (e.g. `Vec::default()` desugars to `Vec::<_>::default()`). - -For functions this is not necessarily the case, for example if we take the function `foo` from the example above and write the following code: -```rust -fn main() { - let f = foo::<_>; - - let b = String::new(); - let c = String::new(); - - f(&b); - drop(b); - f(&c); -} -``` - -This code compiles perfectly fine even though there is no single lifetime that could possibly be specified in `foo::<_>` that would allow for both -the `&b` and `&c` borrows to be used as arguments (note: the `drop(b)` line forces the `&b` borrow to be shorter than the `&c` borrow). This works because the `'a` lifetime is _late bound_. - -A generic parameter being late bound means that when we write `foo::<_>` we do not actually provide an argument for that parameter, instead we wait until _calling_ the function to provide the generic argument. In the above example this means that we are doing something like `f::<'_>(&b);` and `f::<'_>(&c);` (although in practice we do not actually support turbofishing late bound parameters in this manner) - -It may be helpful to think of "early bound parameter" or "late bound parameter" as meaning "early provided parameter" and "late provided parameter", i.e. we provide the argument to the parameter either early (when naming the function) or late (when calling it). - -Late bound parameters on functions are tracked with a [`Binder`] when accessing the signature of the function, this can be done with the [`fn_sig`] query. For more information of binders see the [chapter on `Binder`s ][ch_binders]. - -[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/binder/struct.Binder.html -[`fn_sig`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.fn_sig -[ch_binders]: ./ty_module/binders.md \ No newline at end of file +[ch_binders]: ./ty_module/binders.md diff --git a/src/doc/rustc-dev-guide/src/method-lookup.md b/src/doc/rustc-dev-guide/src/method-lookup.md index 8b49e8d004b..c8d529a32b5 100644 --- a/src/doc/rustc-dev-guide/src/method-lookup.md +++ b/src/doc/rustc-dev-guide/src/method-lookup.md @@ -67,6 +67,7 @@ imported to use an inherent method, they are associated with the type itself (note that inherent impls can only be defined in the same crate as the type itself). +<!-- FIXME: Inherent candidates are not always derived from impls. If you have a trait object, such as a value of type `Box<ToString>`, then the trait methods (`to_string()`, in this case) are inherently associated @@ -76,7 +77,8 @@ to change: when DST's "impl Trait for Trait" is complete, trait object dispatch could be subsumed into trait matching, and the type parameter behavior should be reconsidered in light of where clauses. -TODO: Is this FIXME still accurate? +Is this still accurate? +--> **Extension candidates** are derived from imported traits. If I have the trait `ToString` imported, and I call `to_string()` as a method, diff --git a/src/doc/rustc-dev-guide/src/solve/normalization.md b/src/doc/rustc-dev-guide/src/solve/normalization.md index 8858258b542..99dc20c46b5 100644 --- a/src/doc/rustc-dev-guide/src/solve/normalization.md +++ b/src/doc/rustc-dev-guide/src/solve/normalization.md @@ -1,5 +1,7 @@ # Normalization in the new solver +> FIXME: Normalization has been changed significantly since this chapter was written. + With the new solver we've made some fairly significant changes to normalization when compared to the existing implementation. @@ -52,12 +54,14 @@ before assigning the resulting rigid type to an inference variable. This is used This has to be used whenever we match on the value of some type, both inside and outside of the trait solver. +<!-- FIXME: structure, maybe we should have an "alias handling" chapter instead as talking about normalization without explaining that doesn't make too much sense. FIXME: it is likely that this will subtly change again by mostly moving structural normalization into `NormalizesTo`. +--> [structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175 [structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107 @@ -72,11 +76,13 @@ possible. However, this only works for aliases referencing bound variables if th not ambiguous as we're unable to replace the alias with a corresponding inference variable without leaking universes. +<!-- FIXME: we previously had to also be careful about instantiating the new inference variable with another normalizeable alias. Due to our recent changes to generalization, this should not be the case anymore. Equating an inference variable with an alias now always uses `AliasRelate` to fully normalize the alias before instantiating the inference variable: [source][generalize-no-alias] +--> [generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358 diff --git a/src/doc/rustc-dev-guide/src/solve/opaque-types.md b/src/doc/rustc-dev-guide/src/solve/opaque-types.md index 87531705c2d..672aab77080 100644 --- a/src/doc/rustc-dev-guide/src/solve/opaque-types.md +++ b/src/doc/rustc-dev-guide/src/solve/opaque-types.md @@ -60,6 +60,7 @@ Finally, we check whether the item bounds of the opaque hold for the expected ty [eq-prev]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L51-L59 [insert-storage]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L68 [item-bounds-ck]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L69-L74 + [^1]: FIXME: this should ideally only result in a unique candidate given that we require the args to be placeholders and regions are always inference vars [^2]: FIXME: why do we check whether the expected type is rigid for this. @@ -101,6 +102,7 @@ The handling of member constraints does not change in the new solver. See the FIXME: We need to continue to support calling methods on still unconstrained opaque types in their defining scope. It's unclear how to best do this. + ```rust use std::future::Future; use futures::FutureExt; diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 5e27a2fd770..0ad1d2a28ed 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined in [`.github/workflows/ci.yml`], which contains a bunch of steps that are unified for all CI jobs that we execute. When a commit is pushed to a corresponding branch or a PR, the workflow executes the -[`calculate-job-matrix.py`] script, which dynamically generates the specific CI +[`ci.py`] script, which dynamically generates the specific CI jobs that should be executed. This script uses the [`jobs.yml`] file as an input, which contains a declarative configuration of all our CI jobs. @@ -299,8 +299,7 @@ platform’s custom [Docker container]. This has a lot of advantages for us: - We can avoid reinstalling tools (like QEMU or the Android emulator) every time thanks to Docker image caching. - Users can run the same tests in the same environment locally by just running - `src/ci/docker/run.sh image-name`, which is awesome to debug failures. Note - that there are only linux docker images available locally due to licensing and + `python3 src/ci/github-actions/ci.py run-local <job-name>`, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and other restrictions. The docker images prefixed with `dist-` are used for building artifacts while @@ -413,7 +412,7 @@ To learn more about the dashboard, see the [Datadog CI docs]. [GitHub Actions]: https://github.com/rust-lang/rust/actions [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml -[`calculate-job-matrix.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/calculate-job-matrix.py +[`ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions [bors]: https://github.com/bors [homu]: https://github.com/rust-lang/homu diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 17106be4674..459c082906e 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -441,6 +441,46 @@ $ COMPILETEST_FORCE_STAGE0=1 x test --stage 0 tests/run-make/<test-name> Of course, some tests will not successfully *run* in this way. +#### Using rust-analyzer with `rmake.rs` + +Like other test programs, the `rmake.rs` scripts used by run-make tests do not +have rust-analyzer integration by default. + +To work around this when working on a particular test, temporarily create a +`Cargo.toml` file in the test's directory +(e.g. `tests/run-make/sysroot-crates-are-unstable/Cargo.toml`) +with these contents: + +<div class="warning"> +Be careful not to add this `Cargo.toml` or its `Cargo.lock` to your actual PR! +</div> + +```toml +# Convince cargo that this isn't part of an enclosing workspace. +[workspace] + +[package] +name = "rmake" +version = "0.1.0" +edition = "2021" + +[dependencies] +run_make_support = { path = "../../../src/tools/run-make-support" } + +[[bin]] +name = "rmake" +path = "rmake.rs" +``` + +Then add a corresponding entry to `"rust-analyzer.linkedProjects"` +(e.g. in `.vscode/settings.json`): + +```json +"rust-analyzer.linkedProjects": [ + "tests/run-make/sysroot-crates-are-unstable/Cargo.toml" +], +``` + #### Using Makefiles (legacy) <div class="warning"> diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index b0527da7bf5..69f4c864186 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -2,7 +2,9 @@ <!-- toc --> -> **FIXME(jieyouxu)** completely revise this chapter. +<!-- +FIXME(jieyouxu) completely revise this chapter. +--> Directives are special comments that tell compiletest how to build and interpret a test. They must appear before the Rust source in the test. They may also @@ -248,10 +250,11 @@ Consider writing the test as a proper incremental test instead. |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| | `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `js-doc-test`, `rustdoc-json` | Any valid `rustdoc` flags | -> **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? -> -> Asked in -> <https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/What.20is.20the.20.60check-test-line-numbers-match.60.20directive.3F>. +<!-- +**FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? +Asked in +<https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/What.20is.20the.20.60check-test-line-numbers-match.60.20directive.3F>. +--> ### Pretty printing diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 31e3825f567..a0aa8bd3e77 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -45,6 +45,15 @@ Some additional notes about using the Docker images: containers. With the container name, run `docker exec -it <CONTAINER> /bin/bash` where `<CONTAINER>` is the container name like `4ba195e95cef`. +The approach described above is a relatively low-level interface for running the Docker images +directly. If you want to run a full CI Linux job locally with Docker, in a way that is as close to CI as possible, you can use the following command: + +```bash +python3 src/ci/github-actions/ci.py run-local <job-name> +# For example: +python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux-alt +``` + [Docker]: https://www.docker.com/ [`src/ci/docker`]: https://github.com/rust-lang/rust/tree/master/src/ci/docker [`src/ci/docker/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/run.sh diff --git a/src/doc/rustc-dev-guide/src/traits/chalk.md b/src/doc/rustc-dev-guide/src/traits/chalk.md index 78deb367506..844f42b9879 100644 --- a/src/doc/rustc-dev-guide/src/traits/chalk.md +++ b/src/doc/rustc-dev-guide/src/traits/chalk.md @@ -10,7 +10,7 @@ stream and say hello! [Types team]: https://github.com/rust-lang/types-team [`#t-types`]: https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types -The new-style trait solver is based on the work done in [chalk][chalk]. Chalk +The new-style trait solver is based on the work done in [chalk]. Chalk recasts Rust's trait system explicitly in terms of logic programming. It does this by "lowering" Rust code into a kind of logic program we can then execute queries against. @@ -30,7 +30,7 @@ You can read more about chalk itself in the ## Ongoing work The design of the new-style trait solving happens in two places: -**chalk**. The [chalk][chalk] repository is where we experiment with new ideas +**chalk**. The [chalk] repository is where we experiment with new ideas and designs for the trait system. **rustc**. Once we are happy with the logical rules, we proceed to diff --git a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md index 69db189350f..e8ff3a53078 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md +++ b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md @@ -47,10 +47,10 @@ fn bar(foo: Foo<u32, f32>) { In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`. -**Note on indices:** It is possible for the indices in `Param` to not match with what the `EarlyBinder` binds. For -example, the index could be out of bounds or it could be the index of a lifetime when we were expecting a type. -These sorts of errors would be caught earlier in the compiler when translating from a `rustc_hir::Ty` to a `ty::Ty`. -If they occur later, that is a compiler bug. +**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. For +example, if the index is out of bounds or the index index of a lifetime corresponds to a type parameter. +These sorts of errors are caught earlier in the compiler during name resolution where we disallow references +to generics parameters introduced by items that should not be nameable by the inner item. [`FieldDef::ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.FieldDef.html#method.ty [field_def_ty_src]: https://github.com/rust-lang/rust/blob/44d679b9021f03a79133021b94e6d23e9b55b3ab/compiler/rustc_middle/src/ty/mod.rs#L1421-L1426 diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 0f45c9dbd4e..670e4bd1be6 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -60,16 +60,18 @@ - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) + - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) + - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md) - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - - [riscv32e*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - - [riscv32i*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) + - [riscv32e\*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) + - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) @@ -78,14 +80,14 @@ - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [sparcv9-sun-solaris](platform-support/solaris.md) - - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - - [*-unknown-hermit](platform-support/hermit.md) - - [*-unknown-freebsd](platform-support/freebsd.md) + - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) + - [\*-unknown-hermit](platform-support/hermit.md) + - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - - [*-unknown-openbsd](platform-support/openbsd.md) - - [*-unknown-redox](platform-support/redox.md) + - [\*-unknown-openbsd](platform-support/openbsd.md) + - [\*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [\*-uwp-windows-msvc](platform-support/uwp-windows-msvc.md) - [\*-wrs-vxworks](platform-support/vxworks.md) @@ -96,13 +98,14 @@ - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - - [*-nuttx-\*](platform-support/nuttx.md) + - [\*-nuttx-\*](platform-support/nuttx.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index f7e831dc8bd..c62ca9fd9ad 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -134,7 +134,7 @@ As of `2025-01-02T`, the list of known names is as follows: - `unix` - `windows` -> Starting with CURRENT_RUSTC_VERSION, the `test` cfg is consider to be a "userspace" config +> Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config > despite being also set by `rustc` and should be managed by the build-system it-self. Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7b07d991992..deeabd810d3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -313,10 +313,12 @@ target | std | host | notes [`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux +[`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc @@ -332,6 +334,8 @@ target | std | host | notes `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support `mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat +[`mips-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (BE) softfloat +[`mipsel-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (LE) softfloat [`mipsisa32r6-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Big Endian [`mipsisa32r6el-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Little Endian [`mipsisa64r6-unknown-linux-gnuabi64`](platform-support/mips-release-6.md) | ? | | 64-bit MIPS Release 6 Big Endian @@ -407,6 +411,7 @@ target | std | host | notes [`x86_64-unknown-trusty`](platform-support/trusty.md) | ? | | `x86_64-uwp-windows-gnu` | ✓ | | [`x86_64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | +[`x86_64-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 64-bit Windows 7 support [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support [`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md new file mode 100644 index 00000000000..92780cb5a5c --- /dev/null +++ b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md @@ -0,0 +1,108 @@ +# m68k-unknown-none-elf + +**Tier: 3** + +Bare metal Motorola 680x0 + +## Designated Developers + +* [@knickish](https://github.com/knickish) + +## Requirements + +This target requires an m68k build environment for cross-compilation which +is available on Debian, Debian-based systems, openSUSE, and other distributions. + +On Debian-based systems, it should be sufficient to install a g++ cross-compiler for the m68k +architecture which will automatically pull in additional dependencies such as +the glibc cross development package: + +```sh +apt install g++-m68k-linux-gnu +``` + +Binaries can be run using QEMU user emulation. On Debian-based systems, it should be +sufficient to install the package `qemu-user-static` to be able to run simple static +binaries: + +```text +# apt install qemu-user-static +``` + +To run more complex programs, it will be necessary to set up a Debian/m68k chroot with +the help of the command `debootstrap`: + +```text +# apt install debootstrap debian-ports-archive-keyring +# debootstrap --keyring=/usr/share/keyrings/debian-ports-archive-keyring.gpg --arch=m68k unstable debian-68k http://ftp.ports.debian.org/debian-ports +``` + +This chroot can then seamlessly entered using the normal `chroot` command thanks to +QEMU user emulation: + +```text +# chroot /path/to/debian-68k +``` + +To get started with native builds, which are currently untested, a native Debian/m68k +system can be installed either on real hardware such as 68k-based Commodore Amiga or +Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAnyM. + +ISO images for installation are provided by the Debian Ports team and can be obtained +from the Debian CD image server available at: + +[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/) + +Documentation for Debian/m68k is available on the Debian Wiki at: + +[https://wiki.debian.org/M68k](https://wiki.debian.org/M68k) + +Support is available either through the `debian-68k` mailing list: + +[https://lists.debian.org/debian-68k/](https://lists.debian.org/debian-68k/) + +or the `#debian-68k` IRC channel on OFTC network. + +## Building + +At least llvm version `19.1.5` is required to build `core` and `alloc` for this target, and currently the gnu linker is required, as `lld` has no support for the `m68k` architecture + +## Cross-compilation + +This target can be cross-compiled from a standard Debian or Debian-based, openSUSE or any +other distribution which has a basic m68k cross-toolchain available. + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Building Rust programs + +Recommended `.cargo/config.toml`: +```toml +[unstable] +build-std = ["panic_abort", "core", "alloc"] + +[target.m68k-unknown-none-elf] +# as we're building for ELF, the m68k-linux linker should be adequate +linker = "m68k-linux-gnu-ld" + +# the mold linker also supports m68k, remove the above line and uncomment the +# following ones to use that instead +# linker = "clang" +# rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold/binary"] +``` + +Rust programs can be built for this target using: + +```sh +cargo build --target m68k-unknown-none-elf +``` + +Very simple programs can be run using the `qemu-m68k-static` program: + +```sh +qemu-m68k-static your-code +``` + +For more complex applications, a chroot or native m68k system is required for testing. diff --git a/src/doc/rustc/src/platform-support/mips-mti-none-elf.md b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md new file mode 100644 index 00000000000..731f0a8c42f --- /dev/null +++ b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md @@ -0,0 +1,31 @@ +# `mips*-mti-none-elf` + +**Tier: 3** + +MIPS32r2 baremetal softfloat, Big Endian or Little Endian. + +- mips-mti-none-elf +- mipsel-mti-none-elf + +## Target maintainers + +- YunQiang Su, `syq@debian.org`, https://github.com/wzssyqa + +## Background + +These 2 targets, aka mips-mti-none-elf and mipsel-mti-none-elf, are for +baremetal development of MIPS32r2. The lld is used instead of Gnu-ld. + +## Requirements + +The target only supports cross compilation and no host tools. The target +supports `alloc` with a default allocator while only support `no-std` development. + +The vendor name `mti` follows the naming of gcc to indicate MIPS32r2. + +## Cross-compilation toolchains and C code + +Compatible C code can be built for this target on any compiler that has a MIPS32r2 +target. On clang and ld.lld linker, it can be generated using the +`-march=mips`/`-march=mipsel`, `-mabi=32` with llvm features flag +`features=+mips32r2,+soft-float,+noabicalls`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-gnu.md b/src/doc/rustc/src/platform-support/win7-windows-gnu.md new file mode 100644 index 00000000000..180a1dc6d26 --- /dev/null +++ b/src/doc/rustc/src/platform-support/win7-windows-gnu.md @@ -0,0 +1,48 @@ +# \*-win7-windows-gnu + +**Tier: 3** + +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-gnu` +- `x86_64-win7-windows-gnu` + +## Target maintainers + +- @tbu- + +## Requirements + +This target supports all of core, alloc, std and test. Host +tools may also work, though those are not currently tested. + +Those targets follow Windows calling convention for extern "C". + +Like any other Windows target, the created binaries are in PE format. + +## Building the target + +You can build Rust with support for the targets by adding it to the target list in config.toml: + +```toml +[build] +build-stage = 1 +target = ["x86_64-win7-windows-gnu"] +``` + +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for this +target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy by using `build-std` or +similar. + +## Testing + +Created binaries work fine on Windows or Wine using native hardware. Remote +testing is possible using the `remote-test-server` described [here](https://rustc-dev-guide.rust-lang.org/tests/running.html#running-tests-on-a-remote-machine). + +## Cross-compilation toolchains and C code + +Compatible C code can be built with gcc's `{i686,x86_64}-w64-mingw32-gcc`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-msvc.md b/src/doc/rustc/src/platform-support/win7-windows-msvc.md index 45b00a2be82..77b7d68212b 100644 --- a/src/doc/rustc/src/platform-support/win7-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/win7-windows-msvc.md @@ -1,8 +1,12 @@ -# *-win7-windows-msvc +# \*-win7-windows-msvc **Tier: 3** -Windows targets continuing support of windows7. +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-msvc` +- `x86_64-win7-windows-msvc` ## Target maintainers diff --git a/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md new file mode 100644 index 00000000000..eab29a1744b --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md @@ -0,0 +1,6 @@ +# `emscripten-wasm-eh` + +Use the WebAssembly exception handling ABI to unwind for the +`wasm32-unknown-emscripten`. If compiling with this setting, the `emcc` linker +should be invoked with `-fwasm-exceptions`. If linking with C/C++ files, the +C/C++ files should also be compiled with `-fwasm-exceptions`. diff --git a/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md new file mode 100644 index 00000000000..b7a3aa71fc4 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md @@ -0,0 +1,24 @@ +# `min-function-alignment` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232. + +------------------------ + +The `-Zmin-function-alignment=<align>` flag specifies the minimum alignment of functions for which code is generated. +The `align` value must be a power of 2, other values are rejected. + +Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions. +By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag. + +This flag is equivalent to: + +- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn) +- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions) + +The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align(<align>))]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`. + +There are two additional edge cases for this flag: + +- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes). + A `min-function-alignment` value lower than the target's minimum has no effect. +- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error. diff --git a/src/doc/unstable-book/src/language-features/default-field-values.md b/src/doc/unstable-book/src/language-features/default-field-values.md new file mode 100644 index 00000000000..3143b2d7cae --- /dev/null +++ b/src/doc/unstable-book/src/language-features/default-field-values.md @@ -0,0 +1,93 @@ +# `default_field_values` + +The tracking issue for this feature is: [#132162] + +[#132162]: https://github.com/rust-lang/rust/issues/132162 + +The RFC for this feature is: [#3681] + +[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md + +------------------------ + +The `default_field_values` feature allows users to specify a const value for +individual fields in struct definitions, allowing those to be omitted from +initializers. + +## Examples + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +struct Pet { + name: Option<String>, // impl Default for Pet will use Default::default() for name + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +fn main() { + let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 } + let b = Pet::default(); // Pet { name: None, age: 42 } + assert_eq!(a.age, b.age); + // The following would be a compilation error: `name` needs to be specified + // let _ = Pet { .. }; +} +``` + +## `#[derive(Default)]` + +When deriving Default, the provided values are then used. On enum variants, +the variant must still be marked with `#[default]` and have all its fields +with default values. + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +enum A { + #[default] + B { + x: i32 = 0, + y: i32 = 0, + }, + C, +} +``` + +## Enum variants + +This feature also supports enum variants for both specifying default values +and `#[derive(Default)]`. + +## Interaction with `#[non_exhaustive]` + +A struct or enum variant marked with `#[non_exhaustive]` is not allowed to +have default field values. + +## Lints + +When manually implementing the `Default` trait for a type that has default +field values, if any of these are overriden in the impl the +`default_overrides_default_fields` lint will trigger. This lint is in place +to avoid surprising diverging behavior between `S { .. }` and +`S::default()`, where using the same type in both ways could result in +different values. The appropriate way to write a manual `Default` +implementation is to use the functional update syntax: + +```rust +#![feature(default_field_values)] + +struct Pet { + name: String, + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +impl Default for Pet { + fn default() -> Pet { + Pet { + name: "no-name".to_string(), + .. + } + } +} +``` diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 2d155bf0b10..f29e1e4d27a 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,11 +172,6 @@ <!-- tool-rust-docs-end --> <Directory Id="Cargo" Name="." /> <Directory Id="Std" Name="." /> - <Directory Id="RustFmt" Name="." /> - <Directory Id="RustAnalyzer" Name="." /> - <Directory Id="Miri" Name="." /> - <Directory Id="Analysis" Name="." /> - <Directory Id="Clippy" Name="." /> </Directory> </Directory> @@ -284,41 +279,7 @@ <ComponentRef Id="PathEnvPerMachine" /> <ComponentRef Id="PathEnvPerUser" /> </Feature> - <Feature Id="RustFmt" - Title="Formatter for rust" - Display="7" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="RustFmtGroup" /> - </Feature> - <Feature Id="Clippy" - Title="Formatter and checker for rust" - Display="8" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="ClippyGroup" /> - </Feature> - <Feature Id="Miri" - Title="Soundness checker for rust" - Display="9" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="MiriGroup" /> - </Feature> - <Feature Id="RustAnalyzer" - Title="Analyzer for rust" - Display="10" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="RustAnalyzerGroup" /> - </Feature> - <Feature Id="Analysis" - Title="Analysis for rust" - Display="11" - Level="1" - AllowAdvertise="no"> - <ComponentGroupRef Id="AnalysisGroup" /> - </Feature> + <UIRef Id="RustUI" /> </Product> </Wix> diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 8aeebdde7bb..99e88f878fb 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -314,12 +314,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { PatKind::Box(p) => return name_from_pat(p), PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)), PatKind::Ref(p, _) => return name_from_pat(p), - PatKind::Lit(..) => { + PatKind::Expr(..) => { warn!( - "tried to get argument name from PatKind::Lit, which is silly in function arguments" + "tried to get argument name from PatKind::Expr, which is silly in function arguments" ); return Symbol::intern("()"); } + PatKind::Guard(p, _) => return name_from_pat(&*p), PatKind::Range(..) => return kw::Underscore, PatKind::Slice(begin, ref mid, end) => { let begin = begin.iter().map(|p| name_from_pat(p).to_string()); diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index e64baca974d..7a95d33723e 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,5 +1,6 @@ use std::mem; +use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; @@ -306,7 +307,12 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::ProcMacroItem(..) | clean::VariantItem(..) => { use rustc_data_structures::fx::IndexEntry as Entry; - if !self.cache.stripped_mod { + if !self.cache.stripped_mod + && !matches!( + item.stability.map(|stab| stab.level), + Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. }) + ) + { // Re-exported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, // however, that a re-exported item doesn't show up in the diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index af39d15f671..881df8b0050 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -357,7 +357,7 @@ fn sidebar_type_alias<'a>( deref_id_map: &'a DefIdMap<String>, ) { if let Some(inner_type) = &t.inner_type { - items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); + items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type")); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { let mut variants = variants diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d74dcc98cb0..96ca96ee6bc 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -865,11 +865,11 @@ fn main_args( } let krate = rustc_interface::passes::parse(sess); - if sess.dcx().has_errors().is_some() { - sess.dcx().fatal("Compilation failed, aborting rustdoc"); - } - rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { + if sess.dcx().has_errors().is_some() { + sess.dcx().fatal("Compilation failed, aborting rustdoc"); + } + let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { core::run_global_ctxt(tcx, show_coverage, render_options, output_format) }); diff --git a/src/stage0 b/src/stage0 index 23ee3304b42..bd9a753c211 100644 --- a/src/stage0 +++ b/src/stage0 @@ -14,456 +14,468 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2024-11-27 +compiler_date=2025-01-08 compiler_version=beta -rustfmt_date=2024-11-27 +rustfmt_date=2025-01-08 rustfmt_version=nightly -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.gz=58e2ba5f6a388a5bc9ecfa1c7ec4ba3efd9662e1875ea5fba484c75c5f930227 -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.xz=95f3ed14a6e3093cdee83f80227c7966783fffcc0ff0be6ef095df0dc744b9f8 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.gz=687b58e25baa685be9357048cc1e0c469c25dc6530db55e0179234615ff4d289 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.xz=694e2405efb25c53575599ad6573d8c4646a168e5d4bde922ef5e7d43f3e530c -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=4b42d00e701f90c1fff9a692f1bc1da957fcadbd1813c3066dc1f1990bceadcc -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=4f83a8cced878c03aea2d6cec7e1d588c2b6590850664f28f3b49a3be3a8b827 -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.gz=bae97ca3778fbaa40f6db4da4f6da0b2bb870e9880165fc3126363f55a1fb23a -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.xz=3e617e866a8c03fef243dede8c086d085a6564aeeb5570bada62db5a16e95f2a -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=96d2f23814db8286de9ffce7bab033aa4dc656f83aa39418c8f877a5befbb100 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=0433a866c4d8a434990746216232759345d3ef556b0b92358b231fc1e727962e -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=6a776c6eb2b7b4992eed6a127d11b8845695cdc5d23938ced2267f88f8d97033 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=2649e0f522755dcd511a6459258c3c3a24808fc49d3513f3d9098b17bd0f2c79 -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=02a65e970ee930b46c2d10e1eb3d2e6f2420e08bbef039f10a8311baf57f274f -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=3843610c7ecf98948a5f78a364c223d520a084bc8c49ec0c3c8ec48bad6c0bf8 -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.gz=75288ca1e65157b8902195ec2fff31015017c90d3b8f51890f2f851529b200cb -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.xz=5c694e50b41cd6f2c7b59b891eda3e728a1a2c56617b96c9da001b35885637b4 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.gz=6d1a66c090322ccf272a425c779f9165398e6bd9e0c7e5d0a5f262086d43ed88 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.xz=e826fe8be287f8969d11f620da9d01ea0d913eba145fb79ae86bc78674f1b919 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.gz=31e0d9a1ff97d92c391cc913fd865a0c8afd614279654350d469bdf881a1b1a3 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.xz=3b11a6be4863cb6ad420b66558b7745431834d1fac5dbda899a384dc30b526cf -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=59d96cd092a56d12ea796b65225479803edadbdcb483effdfa36e191f73483f1 -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=0714a469cbd7d96118e9260f80fedf34b21234776904e9c8ec5b71c9493dd36a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=25113ca9719a036ae581f68df4dda74f1446b49ababe7bb194510fbd1b296e7a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=25bd910ea722fe2d4a1e66089352c92f97759ec7212af2b9a04431b469ed8e12 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=7a83c9f63d88849c4d691ad1d7fbe7ab5924b5a7d76378ddff5024269be95310 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=fbbb11b4c986186d94b0e00ebc70400d0071efc2ba3b802abdd877d86b8cacdd -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=6223dff24490e3e8506dfbe2b12620155d7c05fce92191cb01f3b28e269bf1be -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=6fb1edea73bcb76d9cb6061d1aba15ee4655a212c1bf96328a225c7c05df3462 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=0b7bc9ec0c40a6203fb62eb71fb6eeab43a4f64a2f7d0d51a1e0d041a216adb1 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=d77a49acbf26b8c9a2a5f79fdc640feb1c81b9fa38f442b81f69127f49c7cde7 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=bdf45a37fad10df4822690ea73840b38ee3e1048878982e625efbad3b2054f28 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=ad6a2f03b2e27711e1ec4eb75502782c4bc0200cfaca2b5246e0bd5b86bf29ad -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b0cc3024a717d33775dbea63e83f317f8826e564ceb4ecb7a6cf23676f66ccdc -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.xz=9d5b44a82803d5de5355ffced6ec1dcb9860950910bba6dc54878ad4da7ff444 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.gz=c7174c35b530453182f5f0b15c6684c030a2ff2770f4cc1a4a4ba75695e53b99 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.xz=c68c26159a3fb4d4380fb25488aa78223989893d7786b8ec947d82ce0cc905bc -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.gz=c97caf7877097d78fe2191e715e3a45d827d28c27b4dabf0747f8ca522ee43f5 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.xz=b7794ecabc42698cb77c1c90d66676a8b1b3632108ab95954ee496ee7d6e5708 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.gz=28068a6e050abe21933920dba3ead2261f7d0c09687d5a86a5db51cca6e72ea9 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.xz=3860d7ec8564019c79551727a3a54c66453a7d654fbb89b8feeae75d9021fa77 -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.gz=6c2a918f8d40ba01e648565c98ea120d32562dd93820e8f2e33abb2aef08e1da -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.xz=5d1edcc6c4be49849c3c2d1c3c173d4a127db3cfb615093e5ee396e0d0c77366 -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.gz=a64c30d82dca58d09bfa38e72bbe1457b48b101157193a3ee33dd85922bbfe9d -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.xz=87d14b146386fe1f9e24fedc25e1bf9682913bbd7537047b0baef0d74cad5473 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=3f186913535ac59446e249f4d3e35e3d93a3f8207b31ee31b3f70af8344856bc -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=0c17a60ebac74a9cdfc2ec69fd71a21e370ff178f4c83be6020642b55b6d78ab -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.gz=327973698dd0ffe9b2d56b3b5929f82ec552f2002b4113fc9a738e27b0005ac0 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.xz=d7e2f768922ae36a9eb302667398ee308c13f829c03235682529fce90253813d -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.gz=13f327974170a0423ea6e7241cd3a454ab8c39b657a707c08f2e306a7a657b45 -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.xz=5c75388802c0ddcfa51336883f4039b6f7963e0efbe47c209bb455c12951990b -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.gz=84c4c02fdecc669c5d26c49120fdece5899133c098f5ef791ea19aba33936dea -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.xz=7ea047840073e6ad3123a61ca799bc56c951edbb71cb8495ecbc2029759a0190 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.gz=dfb8207b4b3bc13a9704fd1d212d92a22a0e7a57293f24f28bcaa846ad4c08b6 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.xz=96940a84d95b4cfdde84a501758c9dd83f5d6cb5afce9142917d5cb3dd75c040 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=4ed66daf59e9a8b61d66caef4b2248be72049282518d7526be771301bee2b905 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=013d59e32097917b269c45a837bff380a41c4f6401b0c145264cca5a1bbd91c3 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.gz=270a0322dd3c2342745faa8797f4f64da8c0028b322385d209ff65cf04965a47 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.xz=9d54f7f51c99f17ba9cfe7cbe005ddbfe598f8af6df1dfccc9ef37029cc08b41 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.gz=9ae4c1d6044b2c3586791f127108e308e0a381e8e14ecc20b3830ae5258a03c2 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.xz=689225ea098a88a88e8e2269ef53f964405a6c31b3ee2bda0fef98da4ed9178e -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=f1d9c996cbe468ca93b15c76b84c544930d7452f1eff2968a298c885203f14cb -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=f3f8c088d74c81b2457ddff57008cb908cc86e16d0fac60d10969e45210d88d6 -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=fb4e5d66911a47bd63429686885cd5c7f0a6310b25622f36bb43a754300526be -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=ab6c7d346bfd72f06fbbc198743bda35fcb7fd1e131c05c54935b6ceff282f17 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=32b2e0d5161f7d8354d846c91e5cb25530e78351dfcff4ebf0a6fba669e2c483 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=656fc4a8af570b28b3f83cd59d4e5226f15d11b5d8c26b58ca2c1b2a4e18c4df -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=13c98efcde78c1e187f728509f2950a0a407ba32e730877cb9ca4cbb4ba01469 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=6372891715046375fd0a04e80c95bf79080db2d136e187b02ea96647e9281407 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=db156e0d5db6f04621e625e92323a04091c049a8b5e2b4f7e0fb2e50b81ab2ec -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=b610214bb59b8f58be96b84b0f13e5f50473e21039f6e827fa16c446d0e9ccba -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=99f96b0bda31bf21853c96b3618408587f275f46ea9bd7d1fc741e58343a5625 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=dfa1f209f546b457aa6ef742a645f00f55c70fde86522c737f7a560494170157 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.gz=4735b29991a2337a7e2016c5a827635e17ac149372efafc0442a86b0dc0759fb -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.xz=fead0635c572aa4f5e5564e497f1b7e892217ebe52fea54aedacbefb32f6343d -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=bbb9b6fedb874a61d99d22b4cdb2d20d8b9e6c69924922312692aa526851b960 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=9e0d48236676dc9947565daf0c2605316c5b4abc06bdb1c343559c46b9c4e304 -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.gz=e230ffa3d46dbe5330416cdf8b7235b8dc2cf119ef8990b422eeebd017ebd17f -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.xz=38a5560575740f92cb8664d204924cf7a2972e0f2f396852d8a22befd2cdd848 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.gz=0b0a9f95d10484207c52fcd8ab1bd56903b53f18ce46fe263d5b7bb085044246 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.xz=91f7655ab2bce397a0821edc0f523dbf2bac0af39a50ead106eb1de637ec5c44 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=e8210f03bc18661954f147dedd0b4af721d005866c0c4fea1e91594cd6c9bc2b -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=56a623817e6a600e802727d59d4cd653e84cb628695e39ecb23ae99065113115 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=310a408bde58335d8011af480d4f8c38745865772d71631596cef1a79f9f4fc2 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=fd6766ea9e828adc2e43ff1654a459785eed05ad00c7f6804c742ff8ae8f6b8a -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=e663e30526d85d89f41456a3d5bcd411397cb69e7cb9508aa1472b015963fd18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=0361504ada464a7d21e7ac66fdf9e1e820695f3a8ca4854695e78a748c5c7a18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=c375be97e626900d809390c61bf6f2d689c9fca8e58c85e4e02b694bed4c5ce0 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=c33e3b4f9098860f75fcc9a8f86c7e7173692ad4c19cbb2c0de068961f87c192 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=3f50d9afd9df4db85958e4575429dd4c80b6702a8d91c3f1d9327345aaadefe0 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=0016d3a6ec8351ee9fac2eeae358a0854a3c2b360822bb3ba670255e8f27f57e -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.gz=f23117b972ccd21c0a6f4db4b544401045c9f157595a297fb56d7e864cf190fe -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.xz=ab9be4bb25b1575af276cbec7fb1a1f8c683741b0f137bca7b4b61ab13575645 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.gz=673561456e26bc992cb6a888c34c0b6b13e1bd0d3e0764360a785a6c3369f0d0 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.xz=29c6f01c636bcd901b88e92c60874c454369efd41f690d74b1fa2adf3450166a -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=c214d4439676a19c057324dc736d63aa0328ac74c0eb9ced8d782d8bc89757ae -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=4baf85ac632750c35d007bcae8f23720208c5197f4a1d49b0a85b2b0e137f603 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=a7e81c0e80f2508c81ba9d8e658dabeb19af2de6f7365f6e377d39e99d8449e7 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=3f37f746aaa03107573a3a2b201c15bf86777ec405dc03640e7d3f24f718f67f -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.gz=fe7c7ecb8a8e612caf0e7ecf04d18ccaed31d8b93911ded6b3cba0c78fcbbddc -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.xz=d571b714821de4b7d2accad221b5e043e7f9aead8dbc06586f4f7ff3f3f3e38e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=663396f0e4c45bfc041b1a502b64627620f499ca6343664633761c3fb1c2229e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=4d8c47d74958e3b1058ab721e9c6635774afad0252f251b0c9fe4f0895d0c81e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=3cba82809bc1204b6e8f62877c5e9f9a08f111da7d439032cc30d55be0e88fd7 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=d525a4dde496e72fd18c6f2a525756d652ced436007f513267e9201d8b1c0a40 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=ec0311a0722f205b117160817b2137e9a73f0f4e540930d61c831ceba1265f0d -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=c63c4a28225dfca26baab129f06d8537fae5710083046f203a2fb73c50b48671 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=52eae1bdf37366bf406cff95e5e7979d0651979ae5ad4c036e8badddc8ec8b82 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=542791fb5cd80fe5515ab0812c9fd8671f083151d4262fc76d26f7723ba961e1 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=91697f75c762bdb51f19348953768a45bc37c389c2e904a56f4f092604ed2d1c -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=a79a7a5974b365f70598c413da3db8262d14414e3b4d48dde45f529bca4c6ef8 -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.gz=6eec497ec2aaf6c5beb8e6dfbeaf32190b6f7206aebffb7499b5fdd7e5e7643a -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.xz=234924a6746fed2efa112b0b805d0ca56252b6cde82e6422774871e9c009fed6 -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.gz=1b0cd107412fae22266267bd8eccd2b39a36afa8936da3888d0bd0d2f4cfdb1c -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.xz=0d0d1ee48f9032df88ea7b3f73f06300686f4d5b9fc16f1d4554caf8356cfd74 -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.gz=72f0549de44925bd08286e092261164dac47826124123e3fe707411a3dfa756c -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.xz=eb45acd55dd70be3670de83c8cbef5b6ef05827b3f97b7c71679a19c88f8f3e6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.gz=edd8dadbba618f2a786dfcc68e4a5c7f2b7cf1528d1c16ca6ddbc52622df95c6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.xz=ae59c5d77b31aeac30c22104abc0abda6b7f725419e9f86db6cae634ededf262 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.gz=8996c94269e4c60270d253ea75bddb9482facc441ee2314a387888924bea1d92 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.xz=f6b59e3d6e7f0e458c048ad4aad9a2da23f3b54aeac20a905d2e00379da1bd6a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.gz=85f22b148be72691214dee620eac582405dc3b8937d2ef7c480c9070b963af3a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.xz=4b48538541dd20fad8569cb3f0994d5ce6aceaacdd8b319d00b0234cefc5c267 -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.gz=6e5ce19fc4cd8cf4dbaca8b31f4032b08ee413ce20f354186e4945efd4e8b6af -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.xz=5e78b98abdf2b7348381863052c109beec025a5f68285ce72e6f6e9245c22ce3 -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.gz=50057f990d1127e7d693b1c5b9539b19262a478130bed51dbc2837d61d2db6da -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.xz=ea524b276e42a1cb918625df12b8d3397ada5ecd43bb9ad5c676747e1f916c8b -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=c7c9093e277187590b86ea02fcd940071c8bce93a894396d73953276ec58eb1d -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=a67879ff0dcc591d30a155fd972149953eaac3b139bec9aca3a383dc277ddbc6 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.gz=a086c8272f9c7c0db3d5de37b027478dfc22fc577c613254a821700c645df3c9 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.xz=8ac4b938e0b643e47d6d0ba6e5283887fcc73cb1a825fc065c35879b085d9c28 -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.gz=3614b95b54ca24c598e3ed6e14aaa86f7048c2a5be2b3e9f97d9ca00f3d890ce -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.xz=6d36127f5c113b01300fa0d54062bc1137b51d2514346c8d84bafb9f2c56a972 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.gz=a6d33a96eeb667f3f4649ea6ccdd6212a183bce85f095ff7db5036c3a50d35a6 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.xz=bab1dada484d61bb96e2834da5fc6ed5d4d788e6636849e9802f9fb6bff45d55 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.gz=0ac331a1c9e384430a5f316809bffdf01bae3ae9cc1f807f07142ffde7388602 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.xz=136341bbf4c820dfc98e96cee41fa0e9fcbc7d81d2772407efaf1d4be6d10f7f -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.gz=32f06ca3b2752a8687e2f282a59823671fcec672bffc0775a9fb27da50b6bb11 -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.xz=b2f376089b39a34f3308833ce9bf65200f9665e5c55bbda3e5b7e9c5096b7e67 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=a2ca950893c93be71c7db707230b98674a29c013546e27d95bdf8c3c6eebf9a7 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=c70b58f4583578347e9f0fa20d17bd5fc51d88717095e05d73091209f2a363be -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=ed284c40b6253c022e39718dc2af2e4100ca7e805d35d86aca04937586f26ece -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=3efcabf46e0d02505ebf0926c6c14e0c70838f9b510f2328ccd98dd0fa977794 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.gz=9b9f8418fdf64fef652f15b28a1a1f2f8c63c18da8b323994f10f921010721ab -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.xz=fdc673e4b250424c6dafc18a165ffb592aecd7b897e483af8ca54aa145a0e6a9 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=802d22c1c364a58402f9313c6ba2f58b79f3cf3163842a608b78680fa2b3d476 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=dc4932d91d8a2887d309dbd6317d6c41afd083043f44742138ca7c97f60b931c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=636b19ca036fc7ddcf358062c98f6bcf31e8c2cd035cbb9dc0a6b96c81217f7c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=31f80fc50be95bf665d21fe52b714532ff34ebfcdc0c975bb68f7d2f2cebd0e0 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=f2c83275df652bc5b387525e77a1aa30bac31fb301b304ddd7e6bf402e140c34 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=41358bdb20fe3a97a95fd7b656b1ed03d5b8012354d8cfad2437bc9ef6c7aeb6 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=49b8f286b4abb3b34bd38c92ea3e15efbd1f623d5efa7bcff10711b595363579 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=283f065701c77fa47a2d5413d20fa40989fbbcc75bc938889de6c4f9b6b9e53e -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=d0ee5ba5dd6f94dc6ca981be0d55f2e3f51c2af25877131a7a74e0ab9848dc3d -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=79c78bb31022ec8e7a3fad9823c5d22ac553d389104844110473d1877d8c8302 -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=b953686e3034f2b51033a2630328c8eb5f59fdf8ef4e26189bddb8efb4c482cf -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=fb25ac2e68594d379f915f0555b3ded2b1a7b5a67144e12eb62949f540b0d957 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=fbf5a16a57de3a7e846389aebe18bc7871f43e67eff39fc9b4744b5a445b2639 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=55a92aa5770924a21b706724c15f58d66ef2ca5b630f7025f6ba1416152ef350 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=b880b7209ea604761ad2a46832925d32fc547737b879645fd3cf99ec235e7b39 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=c48cc6a6fda06fab014c0b8fef49006dd32fb71f8e981ba44a0c25d47fbfe034 -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=7d133b6c8d0e8b8ec8b65a74ed8d64a22e2fa5e4f6d4a1e36f9e0ebb8826f40e -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=1c507e003c38115bfffe8701df1ff7aed6ad16ef5ac4b383f84bd2d6aa0216bd -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=111a909b3d1a04b328ca1c1f247ee4eaaa84349eeaa8df77f7901bbc0efc94c1 -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=d29f92ed071c60259feaa8005eb5392da549f8e2069fecb1b77e112f89cf1bbc -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=241b1ec9ef6a89277944c9d8021f521284dc28822c5f80513b6cd94d2bdd1d64 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=9f5b02be846fa2084becc5616f024094d47beca102442ac7a0e3588e0f12ee7e -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=7272eae1625415c0f57bf541dd62c14e02b9946ca1de04615827a1f5218c2181 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=d4bcd0068d3c9e7351565fec57ca68d36c9ddbfe59249a6bd2c56930ebad2257 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=758ccc47004398add2e3cfef52e4839e14dc4d132a215658e576e590401a28ca -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=6902fee1027b0136f8dc5179e69e24ed9e34366e9409129b9ba223043ddadea1 -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=3024570f3e382a57fce0e832c52d080a83d42206d8412b724361bd1f0087ec4d -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=20eaa72b6fa32b9884b43030a0bee554bafa9de3f3b51c5768ba0ff47ec492c1 -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=a3eae70be3fa5b0a56cc88cc6433cdb7c42b3b974caf0d879d21a5219f4f826e -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=1da88d1882d4dd1f15ca97e016f392a01b56951882ae75929023e83fa557d796 -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=387cc8ccaeed6f9c6a8c9d21004e45ac97a9739643a63448eb893e13ed4df22d -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=1b9afe3631fb7847b2a8a8f483fc00ca203ccc18ae298339801dab1c7b06b3b3 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.gz=f9a4d2115d6fce06e0da7a9f579b197d383034c218573e145b3c7dae0aad21d4 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.xz=c352bc631e538d4e68eaaccdfb9dca1c9dc8ff0ef4a75d8ee91016aada39d717 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.gz=d97851ca9193d98192a55aeaf695973ac706e6ba3e198643c200cf410817b424 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.xz=89a6524a3743e5a769a583b4a6bf8a76ef5b45c67b7e39da7ae24b687bed4943 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.gz=6fe614db166618a8f2ccb5807e02884daa8bd8dfded2164f88d79fc688047020 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.xz=91c5e4f4575ab004119797988679077e5057881ad9a7f6790b673dd062a8b7ee -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.gz=d0e05467fb1562ed7d6687a50b7fafa14ba0c2aad328f0e7c19eaca1ee366475 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.xz=0473372df5fece49e58efcd3b2a702bb93e47ec149b38fd5244ad667804e7c63 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.gz=e6b6130a1a571325cc1bda8ddba6b2672104c2feeed5c93c6022bfcc8ba4b247 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.xz=d4c739c42104e04aba55b96a3ad434e11b013ba44649dd3a648cfbb15de83eb9 -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=e427d5d6a53abfb53b436df96457d58eedc648198fce6b1d976b50de18cbc31a -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=11142018c11849c21adaa7096abf50d0c923f3b8f07d67eb96d6860d428e9b27 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=46596d8f2271dd5a6f0f84387ac19d0e81976adfc6050b6aab6cc554102fe469 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=f3d0c9db1c50a018540278433fed2e0ae4bc798aa0dac87cf0f55ad1808618a1 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=c46fc4bf174fc54f5565fa04e80efff4e853642e3e5f0bf5b696a2bd6e7ae817 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=706c10ef22cc16b3d4cab8fc09d8783384313b9f774229470b8ae1dd292850b2 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=b9cec80ed374f7b5f22f1446e3f2e0dacfaa0e2d4d6c631ad66cc42a23e156f7 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=41c67ee95e72a3d10bdf739c4841bd937c4e0b19c33451724c1f603b4bd2a318 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=5ad8603f98cff0d5d015c72f3a55730d75d59680a867c067928bf4586c00d805 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=61a75701f7e000f86f318a04dc6746f029fb933e0ea94944d76c7eefca1f24b2 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.gz=89bfbfea23974ed75a53ff134386992e9c257d3e51a55eb49847aec9c5ba6b13 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.xz=e34d1586bc6767b4b244f98754badda706645fdec7168ddc0a409b9f95f2b18d -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.gz=8a88bb923e138d0015c8f46bc1c3d5f2f5f75f40dddfca72a49eed42c080d1db -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.xz=79a980f2b999c3e3b04d7e327bd2b548007039b45da705847c6da2bfec3ffc04 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.gz=454e2c4b46b4684e591c6e4c0f1ce8e3c44623a29d561be3a481ae008072dbd5 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.xz=4dcf652ab51fe03c4691de46365218dc483cf1f7c2f5a552a38509685a0ca873 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.gz=c2c34d9b3998633cfa145dc2a5568a99d1fe26b91c5be55a08870a1d27db4af7 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.xz=4d5b25e4b5fb71d879f15f320f7513eec1ad078d3ca6f525ebf56b9620bdb5ad -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.gz=e1385a340d3d9a4003cc456156bfbb1435a5255b2bdae3a40b6d7339768bdc81 -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.xz=245e7914174d213ddbb550b09fff200f419bcd632de98548a470013891666410 -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.gz=d5d924ea001f914bf87df8765b013db6e838a930a4865e95f8d35bfaa86bc81e -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.xz=bb98d0d55f38f2308bf99d4e5f11842683452571075eefa5b4e1f18f2bd553bf -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.gz=edd5e7ea230a13be750dd74c5f7a7dee20db8683f7978aeb352307a0ce110b9d -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.xz=c6cec9a501d9873e3c7abdefd5b9418f7250c8cb915be8b816cb48dedf1f201e -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.gz=e970c2a4ebfb69219dc66e4c7541cf22c52da5f94983c7c914df1f93beda3d31 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.xz=594ae9bf5f1501322d986dc91249a48a61c22371513b872fd63e134097ac55ca -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=d57bf579b4629417dbe1c02506db4a4ba4428871a2684141d21303e6c99551f5 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=714db83e7563a5f286fb37bc629b64834a9ed981bb9a05931122072f95e39bf8 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=feafcba9c46ba3e5fa0bd9b5bedc05ef3423c9962dc4d25a7b2367e3e1caa679 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=78fbc8f45df64dca21da7223b5d081111922f2268fcc4f1f83595d159d310bce -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.gz=74fb57da7f2907afbaa2a7a73847273ef81e2d6902a326f1ae87767677a864d3 -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.xz=3216eb73d9c9effcd2a2de7f63d78055c3d7e50fb8141e2910a45a36ce744ffb -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.gz=3df431c8941ac28906c3a6568d3debe478d9349d868ea8bd3ad79e65792c20e8 -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.xz=548c2a6104148bf0da21bf5e009b80522fd15539077a62021a64f16282815a5e -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=235228431d67feb4c8e6aad44e20bf68574f335e8f948808b597ae98d827171d -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=5a2d4ed70aed26f902cf45e1dcabc5f25884cf6e9aaff775405ba9c1f0a5cacd -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=818131158e8be6af4b762750f7927c4a799346c94059a6073230da79b4408720 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=fc08b39833e2c9319dcb55cef5c6d1f0110aea794c9a6f163ca39749a22a94b9 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=6164c3ade38c03113193301b815c52df0ee84a4ca1c8ef21e1a56d386e28df00 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=0846126e6c4cf2b5184979b6025fbb7bddb5c22905bc7ba31fff69f6b1459280 -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.gz=ae308e29be13a176b26c142347a81facf8c11ef1a28e67d5898f4f2a0d3344ca -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.xz=e80bcba8b011a4dd3f7b2a2cea69b5a95ae2cde9b2d9078ac8fdd492ac162139 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=b25f0d605b7f4a2a93adb746e32f59bfc26af569a4169e9fb203cb33751d1575 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=010f35236062a651b1b54d7c928068efca8dc486856b84b04e98f4f97583bf32 -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.gz=db3d258c2caea73b469feb9c11fbd3bcc29d22c3969c406321d11f05c24974ed -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.xz=a952b0c2bbed9085525c041c957f00b6b1020e031149dba70c09a1a0d40db3ad -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=817d3d3a931eba2255bd5721435cba7604fe37a72c18a179c1fd5f06dc3b97a1 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=170a54aa50f161576d4b0d103f2ab6dcbbb81ee57a5a47f7fbd181128f6c17ff -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=ea991dce55415ed7462abbeb1ac1c70795cb628b0460e14230e8cecd41117fca -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=a8f620c3a641f8f4c29d88e4cc54d7637aeb18cf8eaed8571ce558835f742601 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=875e2e3309b1deccc1a4476e4847a2849df32e0e2a394e4ddf3b4abd1feca534 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=23f21ac664b3341771109ac69d094518a512391b86219ffec50725dc68a08504 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=680f5b2c63f58643bd34f9921e2c1699a549410ab06bfd04ce991907e8fd7d8f -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=dd10f6f3ce39489b6e3f3f638573e850c36518e3b2e8ffad0c50c3143e3a8124 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.gz=fd31ba490f0bfc53530e34e6e5ab35a52eebee616efc8ed18d473065ea885230 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.xz=daffc01050f5cf0b5ece91717d7a4c42cd4a148c4c11f641b494d9c375df5177 -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.gz=376d571641a9f1f75fadf951da91bb73ba94a5495d7d33389f5f914ccbec57bb -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.xz=ffd9e826537dc94c6d8e767ef5a61d1fcf0385b8ccbe508ff9177de0fe772939 -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.gz=447e434b5e40f7882d7adb6d0346629e0e3e92d0f339c7781e5a437898a84b4a -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.xz=bc21caabcb492ca648e19a8f3b9dc1a1bf7792135a202831deb554ec3ea11077 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.gz=40ca7d9ebcf6610c26f2921c0560729e708f5661bf6167266e9d8cb3987ad0b3 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.xz=06966d5901f5d9accadb47c4d1e561f3f95bd050f3f8e78f30000389f39e9b6c -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.gz=e1a24f081ddd93b483c0f1c196bae676163db50fef0a994bc47c2a7c98c9dfa7 -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.xz=d51a5f0669906eceeee4e09b079828e2ebf3404aaedf1fb20745339205270ee0 -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.gz=e135a5f0f83432b58d9e093c6d560b021a133707e39e1ded90a28a9967105e0f -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.xz=1fbe65015cb8c7ab0b863ffc6f7aa72d3bf52f7830957ede8915c61a22e5b993 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=9d4e830e9ba582302865fcefe31883cef4a471e77d46e70838e549ca11a0fbce -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=233f6347a36e806dcf27ecad4906c90bb3353051f440a819989efcf091cb78b8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.gz=6cd719a7255a0eb1f11592b9a92d0aa4986363f624ee0d6077fcc253acab66a8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.xz=b874ca8285ad1936adc70a9323cf80f06322b1e6d5925f54b58500129830f0ca -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=ec7200319224ccabe814cb051e51d147f1ae8c763413002fd03c9263a02c2320 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=86e96c80a91862e59123272c80f1cfabdca56bfddcd66af4a5e840af7421c4e1 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=3f7cfdcb28fe8c3f0dde58c24a0c424652b40d00a24fc50891351fa5bff5c15e -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=2d5a184b0103ac943ec4f6756fbc7126773e739e0d120acea7b3244c7cd19b1b -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=9a77540943f28584d90b4bcae4e234cc9d833eb732f65d7fbd680a0f4e915da4 -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=7696a065951b7d7179b30ff2fd16a05675b6bd4250f293666b98ec05299113a5 -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.gz=eaff75076ad71a4291af5465dedf0fb0998dc171e35ab8f707ac63b43e89c38a -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.xz=205d3d19726a1a87a1533d94f77b73ed9de8181906acb48088dca22835aa7a83 -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.gz=58da319889533ff7be5c9142c2abdd1ade79de10eb83d7ef82ebd93b75607f8b -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.xz=5ae699d0f6b9e210a988af5b8f2f0c4d62fb1695ed6fa55718fdf05298039b9c -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.gz=362f6338ea929ceb632307031e049dee1b3ed998f5cf160654c327c5e0e7ed39 -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.xz=88f14ca7b5c8af006f3c54749b61747485f7e93fde4c65b5969e73503d12c5c2 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=9ee8f29e3e715cd7c85e679fd01ca724a27503a50edb0e6f8912f289630bc7a5 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=4c643f0d18ba101c2b248e1df32ec3ddba82efc0d5f9b47945d484fd3d462794 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=f70952c63e2eebd05acc326cfa667595535fc36db34bc0262fda4ba100ff2145 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=0546a908cc12884801c35b461f98db01e87a7f92d276ca6cb6b89eb76c596e2c -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=3534e839b34603e4e2b32fb8ecc76163ea3b9cd9907c1a22fde4165860d1fb56 -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=b402a2e73debd81dc5cbc866f00c2ca82f676e2f923193674969af0ab34ad311 -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=fa69011557fdde5af1f78f33f9ba75a1d6f4488b9d8edd2958582593dba347fe -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=679be0bf62c7371b92f1824c8718662672ac3ef2d79e8af5c752fe34effbe333 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=ecc2ba0da5c1057136eb105ec0727e55396e414d75efe0c2b96bef0477961574 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=a89ad44842df861c69c6633ba79bf4ecefc38419d5669b6f22a265dd7be836f2 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=051437ff7d177bdeb7a5a0eb947d27b8cf352c8e809b9311a1cc21b785a5a8b5 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=1a4f50972104a95c404e778279a4acd42176cae45f58d316d601e7f38dc7bbc0 -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.gz=bbe9812fe890b900e71aa4714dabd2f3e67bdc610fe0bbd5aea645ee807dd0ff -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.xz=eebac7e8555f44ea9094750b61bd11d758197a72d4d436e1713e4bca42fa8080 -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.gz=6773b94f012a3d3589e2bca28f35dd23208fb245f107e4a17b8b3e7f893c038a -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.xz=d3a20dbd60545b0162dc06499317e342bea077cff369e053eb89e85e2b640188 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.gz=39e16c97c0b22533dfa674d6e8f5bf5df256e2b426d02dca5ed33e246f18d05b -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.xz=ffd939f901c64a17c3f24fc69744cd2af1c176a45d8e5fa8209067ada403db7d -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.gz=8addc12e74a983f3166b89d1d53ce87c92f38c7c3a62ec7c66580aa424d20516 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.xz=8e5622917395ff50e53b237e7322f1faf2b64f0446c41ccaa0096e57a3e7de69 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.gz=2734d099aa1ada79346213aec8cc7ebe40312df1dfb40a32d09ccd3b6622e025 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.xz=642edcaf16dd74c0935d28ce9b37cf1c5ecb9c03bec30f6ff0e7476760ae7ca2 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.gz=4f28de38005de48db7126dcb698b748063341de41dc71175605301837a3f7336 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.xz=ce18692f99780ec09d01816277ee4e71785bdfbd7abb4375ba8d87c1b6905ffa -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=f1d692c3a1c280f34de167ef26af3c13a6d0a3b5ae4fb86b0a9f2178c3287a94 -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=21f477af2b49ec8809bf8bc7b4c4919c7d2ac57782b829f5a0d9ef1967c5d71a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.gz=4bed61be221c1347b1649f54222b27e2a3577b84816ce1099c9f0f461c6e9e5a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.xz=33f463209d50ca6b04a8953c87d2177a84d1729d635de9a7c9b1a711ccb5d751 -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.gz=ce46b5b2d767cc0d790d8fd21052ecfb787dffb4218a238a339fe5f1afd26d6f -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.xz=0afd0489cb4231edc5bc1ef24a43c3f1918f681f08c1207d184fefdb46416509 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.gz=6e5f1a85dad2b899567369aceac0102943ab807dad7e218c273b7b4d6393dba7 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.xz=f784ac4a6ab180675396adf8256ac2bf2d1e823bef106d1c6c59911a8360692c -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.gz=22f1e9299a24aebaf9a32471ddc6cdbf856b455d059ca077170aecf5989f8772 -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.xz=7f3fd047ed5aa85ea6ca2b0d6103f7530d7d070c93b0f212c9d1a6491f160cbc -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=f383ecb5cd654268f00be1f577572993c38e7386a1e1d18b864338e87c7b2b42 -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=b2043facf587f98a8f10a92ae129b7f608297709f4ac61d683b7c813cc65e41e -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.gz=6678d45195bd2a7b27ed2d4bad3790e6b63e376da71d112b9333760ea375c40f -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.xz=f6c77e80c9ea4e312fc2715a9f66ea9a2e6344e3f04ab96cc572e4c0e8c915db -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=be8c0dfde3f09bf476d6600b530aa498b01415015665429bebed8814c7568b73 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=4315f7623dcf0a278cccb6c8b397a29f83f70ecb9b03588f61e383e5ebaa697d -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=c31ff694c68f28e0f462133e783a5d732cf9c539737fc8dbbfd01a53e9a456c3 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=2553ae5596b8273d8a61ec66d2e20d0c9733c9f791ca5c56217c60884f0ccf9a -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=1842cf2c26ab474df47e74720c56997e270b37609f840ac50ab0b05064cb38fb -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=e882b1cc2814e3c97c33991fa0a80de62556ce8f4f88307f6338a90be32b045b -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.gz=7c6e08b841f21e3b8953e51a6a9fd9df0c719e8defd7b1ab0660d8920fe72185 -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.xz=137aec194877446fec994a233427db419bb4f2910151c456c3ec80d9329628e7 -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.gz=52ba38277ac3f80d124fdc8dd1709b8c1115cecfc53fae4731da8615a63f78cb -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.xz=548d3eb599334ab98791b8ba27decff8b0de98dfcffadc994ad38aad6255cff5 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.gz=448071394d5e647e8da8f64cfbf85e8305d85ff9357dd24737523c95eb339ab3 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.xz=e25daa6264cf5e0a7cbcfd827e27a8e276c29c88c86cc15977c1a1650d0303f9 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=9f080cc937e7209ad83fdb8bf4b2cd7b080af83dbd9061229f154785e273bfec -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=7d537f66195c581ff51ce8f53c4687a762daf225c7f0ce76393953ab04b804e1 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=2bd38261a9ef33ad687b3edcd33f283c32a493a80b56dbefc822c1116edb1f2f -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=7e63dff1d8abe517aefeb4b1976932d23a9a7ed5e4db2398d8ba2659301f639c -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=1495b2a49cfce014da72beb691e6c477d189ef34080acb1761b588d11b75d27a -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=75da94f1ade2ef48674b77ded595c69ee715f62f9d9dd96e72039e27807b7314 -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=2d6b826fb4d3d9b61ecd3aaffdd1334f766dd5a5fb0d8279e0de26d381c5cbdc -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=f5ceaca2651d6002f232f71809458ad912bb5f00030790e907ff7b0e12c9e0c1 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1a62f307197c6fd08fbeabef89f58842cfd59c6d62f530850592fe55852d61f7 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=4c65fd6a5d74527391fbc7c242a2313e5290209f5a15763d74eeaa6cba0a0ed0 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=8ab9f735a98de1949e65d51c3050b29ceba3ddc28167ba0180353253dc726392 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=0367eb23a9349ebd829d59fe0a2638557c8e57205e48f66a701b238461385bf2 -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.gz=ffe9100ec6b81b081d5f9d840bdf01c658790d7e6bc0674e5ebb6b28f79c2f7f -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.xz=b19b1b28bb7ddf8cf977f102acb623f65d5117c44ab8b62cf197f2d3d33c15de -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.gz=d986de5c01bde3469693dea7cb4248155ee9791550aa179d601066d27e45afb1 -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.xz=8000e46ccc2598fdb770c69c68aeb18d94db9b2847d0509f7a9c51ef28714ba7 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.gz=c701581f28d885889ae5c845a7b34beebb04a4631c2740e9333b69390cfaf499 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fc285792e6f626e133edede613a9a9a90ee12191765e2f0beac642a3b3c9ca12 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.gz=91afdb71bdd54e5c04fc7c933dac06d5e769627e886e20eb712524e852a01966 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.xz=2ef7018c7319e3a299c5eddf36748d9d293ae43eaf54662d9855fd211eb4658c -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.gz=02234d4c47478112e01d51bc7dd48cd467293e1eeba55bd383d08bc7376c471d -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.xz=a3e6a0c7f31278866d97816c5ed434b1987e0bc5a89b24283e836402803c1388 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.gz=b238ab536ac963929884da4dc9bab43679e6fa9aba4251c73ab7e5cbe5d8eeb9 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.xz=797ea7a7643a8302e45347f21c4dc6bdee617c2a9d9ccef74ed525c7d2e9dd91 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=d630a6b25a08af85d3d426e73d3231e018e0ab7176a5934572786147fbbf3823 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=d0259a0112f452ee49b8c9bebd760ab8945e5bc3a0d57b1cdc2b8e24accd35c7 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.gz=5febe478e5040f0f74a14c23e78eed21b080900d875d6b447354945c07677a6f -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.xz=552c35bbe76a83439e66cf9ccd522d97e43a8fe4f6aadbabd1f02c4b2c1814dd -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.gz=ff6db2d7b84e1b01c81e494de15ccd07f60fef6dc60fa46d47e128ebaba4022c -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.xz=b27f89b175bee3769dab1c2d0c54f17ff3efba2038f3b600f4077435c7fd21d3 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5b5015483d0a98e9dc715c3cc8c23598c02fc14ea6e5878790ae68f2f1c8ef46 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=ce5706f0397a1b2fd9e17dbf34ccfbc3e8c87cc81c92b54b81fd33accc2b7c06 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3f5077c8743b5c4f233da7d0aa0aa13fc488ef0fa9da24b002bfa413d52dc845 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=087eb30f24ba7d8ee42e28bce2a6cd75e5fb7ef5dca8a738ef23fac82ad59566 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=28a716ef9c5318543559ab4c0c2d062b0deeab1ecec80d5d83ad5effb574f209 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=99cf9ec4b7bea4281f64a613784350a8ac34e9b0387fed7eb26d6f8c21c83735 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=a23c3de89603bd4d1035a267e6ee456c6de12efa4029d76ded3edf7c69285a15 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=462dfd28b631b5a6d8277920cfa8037d8583cbf6a2e5c9159c492161013e820a -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=44af5b13726cab40865580df8bc05182a2359eae64e41a92f84790ef55ca6bdb -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=0a7c0b207e745d01194d96f0fbe2402273d630d4f1aa05001a5f5371c9585a2c -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=66e2fc688b8f746d15c0dce042f2a94e06c5b652d5e0b6534d9e992eec186784 -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=15a2e087f7b482fa8c027fe6e6fbaf846d784c8e917fed375048a297d5e13c45 -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=71788ea2544f8349ec548a2f150dca2afe80a49deb91c00c46484a5e3039742d -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d9fa7b780b9d8b93549abf0a3f0e6f31cc8e1e991ddf682adf3b616fe2a1f0c8 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=30924bad51ffa62f762d4c000f86ea3457b590f6397d65755c711ff7f77ac129 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=14fff13942e6a65927776c49013249b2e75aa3a630667ecf49c9ba721e47bcb4 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=02d82688bdf3bd3c261a4a6d4acfb07835e29ce262cdc3165ba849a6a8490bcc -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=2a19b55bb609542ec49dea87e3b67532e5d18df8be43d0ad775bb34f4f9f96a0 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=d07ca0ddfb0dd89a9cc935b2b9662379bcc9503cb28a28d68c5165f787a7d762 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=de8106801b1e49dfd8a4fffbfc2a4f949218eaa011ca2085953ebf2a5ea9b141 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=309024f86e80a4568721b5bb61aa936e027accc251fa97acd5043b513b325576 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=55bf234aa2ec7fd4c6a7179f779d790e7f62969967519bacfeae84db5cd29abe -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=4bf9571182e7ef40e76c7e5905fab8ac35269ace390f5007480b4951f80cfa3b -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=2a2cde80fcbf68768ba7a99cb059a0cdc9313ad0c934383dde9598d6147ef756 -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=b747fb16b15cff3f9368ce1a1f5cad0d2930bde5a609547a3afa2679d7ab8a1a -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=b861f8361ee806d83f38afbbbf312e0e5e4d8851e7048444c409de1a83484446 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=ebe56700ad0948e4dcd9930730f7df8e01a5eefca1d1157fe12160d782e2a5c0 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=5081c9b8677314464d8a3a50220809def18badb2269d2cd8b7106f3547b0e25a -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=2ed9094b538b6450371311b742f2d309a1f40b2b4c84deb9867059336b76d1b0 -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=d9d52d9b9f4da7a457d05dd4645885d092f170bd2f751a49b4ab3b88395a5248 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=a2e51d381645c6bf18502ca72fe9f10ffd309281dae87ec16c5627f232f77e50 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=c526efc054c33a08626b882eebadf887a9aaf8fd60a4adf6a146659a7c1ec8d7 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=04e5b7c97b57ff7d967abb4f98374f0833108c04f6edf482b8b3fefbde340956 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=1829afdebce1c3fe249f81958de68c765db18b431927e54fc42be37ea4ab8226 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=d6cf138d02e50168f8ee4a89abb2a4a39e19458d060d7b46cf227bba1fd4c0d8 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e8185262c82c064c4bb9ae6f1d3072819410de9cfca943b77605012a546c254e -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=64d78f9f05a978b95b9e22a63bbb31dcf98152df5638f498a361a96d9d2b0c04 -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=c07041ab84364ace58a357c18b5c4dca037852e1159edabb02f4579ac6853b4a -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7ef0122ccd3a0c77c917bd75e93358eb95d7e87d78a165c724e3f0cd90f8209c -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=d0d0566a0a50e1e9e7f4251cf207bde82d96c27016f0a0acc364754561ab4881 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=e4854528c31316894339bfa97b07ec607e8956877c285778e555885ce9c8a068 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=d79c9b352ed7b2f132f06dcdf360527502b2153beb09cca968a5ced521edcd39 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=df76cbeae0a61506a29db22e8743d16fcd97ef2da216ea15bf4d6cd709814017 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=103cbb30bbe92da6666d84fab53dd7fe8105c2ebe62eeab5f6609488e62a481b -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=5f3c61663a319d6162240192f387e10ab87e2a972062198082158a243b667d7f -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=221b45c3928b1c45fedbeea987ad80750d3f55fbc564cf3ccf910a68447ad725 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=18eb24fd665ce1cc4ce66b37b19c22397bff9369963b127137780870c179228d -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=bcd09587a21ea10953a14521e9e0ba7b5100e8e15b6a10cc4e7bd359200d5279 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=db712c5e1d21231770e12dc95f6bcbe2f1d04b9cde61bc7adf8b529b41773adf -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=43303e426752dcd15964416822cdce0e5f3012366a9b77f3d68e6011c7bacd0f -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.gz=95d543a835b11cb5ccbf0578df1ce4e1846e4915622d23a36cc6e18e44e17f29 -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.xz=db9e0c51c35c81c2ec8be0838f3231f7374917215792ffee159e3d7ffed855d8 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=bf9b83d6418bf97caf2df70a8e8298cd8fecb7b950cdaaa2cecbec243ed57c74 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=bb520da5b522fffb5a20eed4390990c2ab9d22eb4f77196535a84ae7a434c9f5 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=4072d9ca2d617d09d394139c0b8608bb8b2b8b2b4fa450f13c41975a16a791c7 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=3d75a8a534e86bb55b63f5b5b2df66ae47862cb7f3ecd1591a829435031d7279 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=3cf05e90bc1f95e92cca4769f74055245234037009cf464df3062238c88bc2b6 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=245ebb7886cfba64d667630ca8bd672520cfece0ccec3b916e3de0f26aeada52 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=dadff4abd2f169f9aa1910d4ea247b7d1f767fca74238ad31485b73399ab6dda -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=ff97e1201e9a305673e8a9b740b7696cc4cb5e86bfaa3e137cd8e3e28cffd7a6 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=0c411e0824140323f51600d387d52bd01f601f5539d6457815e950224a6d29a4 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=dc5731a70c393f69555433d522cde8a52893bfb88beec519c1bbb03b2a0e060c -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=01ef2f07f1222fbf3e0cfc5b957c287fb886fd0bd08cbf18f90dc37fb0b63541 -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d45042b3ea0c0ce6befea7bfdd731e890dbd1f47d1992fe8718a1363f9f18779 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=5437da4c0b73d203789829512d856a66c1697d95afb1ffeaa6347ad0f7c04672 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=a7cb4cb627d75de1ade9211d8ea489ada9ac76ed38909751861ef0cb729dcbcd -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=fd6ee6fc171459092e5e8a012d9cfb7491313e021d1c38965605836b5b101b0a -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=314c7cd558a3654db85d75b3ed71da7cfec3614e4a5f2c449dbc6663b64c7e3d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=7a1118011b11befb7df2df57e4450c7611bb3503b3b3cfece9c9c7d4a304391d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=2cb95208e77546bcce56d8d5646c3fb5e49ed711894926cb50903ba10431a36e -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=4a76c4493e5ba19aaf14947a6c2e28ebfc7f2da530f1441a9fdfa328e15ea4cf -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=9b361904c691e17fbf44d80c7f7789511db1d8f251d65ba9cf6fda7f06fd495f -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=e89dc76ab061ae23081aee1619fcbf4a94e3acefef6b9b149231f3e1c22f9ec1 -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=11266d557e6d1d9155e6f04f65e4815cfbfd9f8e1aaa47221030e3ff11b22f78 -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=30f2eae1b8694607a04c836f750b4b7c204e1e14e52ec37885b9a821c2c9646e -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=495316e70b26140c66b074e9d7f714ab949f422c2635cab784a5e133538e4bb9 -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=ca560c4fe28051573d54f512652ac9740a2d1111aeb8e36b004a6ff9c325925c -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=1191f15bb6da98b01b43f0e9d7f51c5c45d38e3c5be2b4ae5f7c0c8fd25e9a90 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5f85829aaab919fa4b2fa5ac843b87358e8da4797adbb6eeaed5be02666ce964 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=7c671d366fec561f0b568bfb4b6a08a6071303077a60f76da1307453e51ebc36 -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=300307ab6cf88afc4312edaa510769e901598492deec4834176c6fc9f3eef6cb -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=6fcf91c8679f4963f7d510cc8afbc24a1070b25ea6d20d5d31ad78ea659da194 -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=ab905f6f3119648c83a2f37ebe1a55b7899a5ac6d10070e4dbbfb89762a369af -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=13688b6857c8b10f7fb497f07c969d406cb6b76869957b5138f4b20fcc5d7c5a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.gz=e6bb782014e34fafdc5dcf3aeb407eb636d822d346c5a8c6227cc45bc645561a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.xz=6016ae620deddc42d1d6b6134b0164dab45a204089754b73db41ec837aa88a84 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ed3a381858612e25cacfd89c08496e9d06378d593ccedea4217ad4fa1ab326d4 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=4819bb9c25aa743de7ab7e93b10b30336a87f1f75e5122d578df2c83fbc59850 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=404aac87c77871c80f83cd7c59390af9af704ee468f40eba68d224d23e2c84e9 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=0e1e0d2454ad24fe1da2cce2dce425bc1167bbca68832da2476c7996c00ba612 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=946d5bcc3ac0b83fd0b53b60290a6361b179f16ba2aa6a2467789ad520278792 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=f3bd1a053399ec89dccaa7456da41d54a8e91deb5c628e4c502fdcf34c5fe780 -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=c353588a38705b7ae050d4b9868b6c4d3527295edbfb25c115b22cda4912be1f -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=a71d8b2b42f6cd522da237f99492ed59cd34ea8c86fc1e73c8854c8234e1c980 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=5116b949572fd6c7b11a079d9453b15a4c48562bce22b26d82b7050da772789b -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=3635df40363273d44c6f8182f5f6a3a099ed55897bb764cab2565363cd31c1f4 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=5233af0b907fb7b176d2b5fee07c486e92dcbbbab1d54a60d814a827befaa984 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=c908cad6d28657f1fdcbe9d84ecb86f247c75134803dd5273ca24c6c68a58f37 -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=c799165c319218a2fb4cdc0ac8db87fc9e2992a8e402d984ea27938a5ad1c5af -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=ec12763fa5d4cc150f663361cd245c30835e05e3b1928898b1b7300de559468c \ No newline at end of file +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.gz=8869795c998e9d39a7093bf53917291b066e35964059298cb1cc2ee2f1088b2e +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.xz=1397b1ee764dd51d8a082a063b6b13a2d5609b1e1a6e1dbbc0368f24e7830e85 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.gz=68719c1f92c6aa4251c27910c9f788472166444a9295cfa0af6450bc6b5e6555 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.xz=56fca9013ab1646263f043898346dfe206f4b264c37d1cf39fc61807e47c072d +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=7acc2b71f759f098c542f0ff4f3705e73112dfb3b8e85bb9158d1ce10d858efe +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=0d21e19b284d2053879ee55353f0d601e3b359530cfb9c747f6cc4ad9b67ade9 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.gz=25a5b2cf16fb51056c208e5899c9843bc3a902ae2f4148ac53f0b9043727e321 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.xz=31b1dead28d88897fcb2ccdaf72021faba9febe50d797e70ff5c29ae82179618 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=fa8d3c93890425c9a0d130a652d3a4ecd018cb3098efe243a3ce8422539cb9ce +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=15344286d365042d9bd8cced1268eed508aaa6152d5e3e289fbd5ebc87788e75 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=c5ed0dd8c2a3b609d9fdd88b027a1d628517107af8b5488a74bce9a3a4dbebcb +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=ff5eb5bc42dcfedaae86bf31af37c6c2bdb6210e98cbdd780c4e1a63ba37eaeb +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=6108711c53f07920a9afd0e749ff4228396220ec2eeacab2310e5b32af7a5e91 +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=7bd41663ab83aa4bef55f3c44e4e4cd3c4291c967573cf8b2ba3d60eda9d06f7 +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.gz=12ba512d2dece4f9be9ac08f3270bfa6663aa28a3443f1cbd7f852ab052f12ab +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.xz=834cd8f4962401dc522a5a0ac866561a62c646614a6ce4b99e91619098538861 +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.gz=9915fde0d52fcc41eb5f5eb19c06be447187aaf3e5b4f438f7c20d92b364527e +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.xz=e4e79679d6f90b5de5c64a534ff150416e48aed276d7426a1d4b1fc2e517cefe +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.gz=e394e8f0361f0f8a8a9653cab3ade57de10622651675e27c137cef2b06114a6d +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.xz=756965d724f5389a3cb63374b4287ea48e18d38827ac3af61a127fc6561ab865 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=4e3483a31356c4c7c0a7041d318b36e02bcd25ac25c396e4dd139ff4ecad5af5 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=9fcc0531f285832f5333762317ff64dd2506bc263e447f1931d361b15430d0f8 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=780ab392bd8d879efc593ec9f87c1ff927e750fea4fdaae23d327fc363f8078f +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=bfad2585877df4fc99fe708187336631140a8170d21159a6deb7cd8f8ed20248 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=30659e0526394f50ada52ce228cbe342e7ad9f6db2f0e1dfe6c8c63eb7ec3dd4 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=e59cbdad42098b5f402f612c957113e7f7b8f4b4f37292079187f44d1753360e +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=adc19d38f3927cd2c09e2b4bc5e31b1b2473d45027b839abab1837ef22b6cb31 +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=35b15e40ea3b382d67e98dabbb93916abecb7d18977652760e081c1a112300d5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=31477a7164f8b3658bdf192bcb1383bb77f93e684887fc7b06e55bb469e7e25a +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=67f4e6f541e16eafa55e803e2c41fef1c892e7408f61228bdaabfb3a4cb41ade +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=2f4b8b12fc8bd7bd5783cc77e9b798de3ecd4a17f5a09d6fd4f3a528e1a243a5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=474f1a2b2f3a2379da02ba24c59a160a7ab0a2bdb4290b0b277d8aea8e441a53 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=6be8f141e0a3f00abade77cde91405ac107d729f24b946c89831baa674c210d9 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=897be1e29a2b67a9dc2d69195ec1f41439903dd3ad8a7b57eb339344ca6ec5da +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b58ebbaeb5474f577329f4d48991222621d4f30f6411a00973951917586e2cf6 +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.xz=215384ed943e173fcb4e10baa69d6929c99e8eeb4790365f1067bf20d5417b32 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.gz=40a3d42489eb77cc7912af5580632f8f6f27373b0f691d24b7d249ca9bc84a41 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.xz=6f8c83b96ba2d9bdb623af0f09b6227532d8c0c0f6ac19fb4b0c5f5145d2ee9c +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1f76fb03cca2d0fe466c995467f209c28a68d8ad69e7b2fce521f4ace94106f8 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.xz=5bed9303cb18b7c5d78875b11463b945c9c9583c5b377efb69462bda0f92b190 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.gz=a64fedbd98a9d2e8ca6d21018abf627367e2c81a028d38fb850253a6c94ef45b +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.xz=471c040fe9f119e3ddf74fb8887d03cecc415109577a85c14917c00ab3348d0c +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.gz=468f89e4f6b7e72cfeeb19eeb723e723673487f3d1cf333d860623bf454e4ff9 +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.xz=c9e699c5fdbf0af0434354caf8745796db42a931669e733b3a7432baabf81974 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.gz=e0dbd7d05f74a1d444efb849700f98bbe6b00f870036e5cf6f065fc2d0b982b7 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.xz=1936046fd441ed9bf8331f77ac9ba930640523ded0674831912d28f9f7ae90c1 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=c6dbeb9017e7f9694ddb05c4658250945e1d6c2b6de656306a3686d499653029 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=adec507e847d746ef1bdb0464b23ef7fd840d17e7bff787a198fae239b69a77d +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.gz=32714ed814213cc64fb1721c919c43a877286f625289bf5d526e247de0941e05 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.xz=fdebe84ac7370ca3cfa66cff6974c00e506aab6fb3ecb6b70e8e2c3ef92025f3 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.gz=6443e8f6c2cd4d5f4a0b7f38e1c741061f42c454d006afc06ab87968425a8f44 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.xz=c12851a0437c7ba6edf08d9ec89f2ccd22f05a5de661664142bf4f15d2a658a2 +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.gz=e88734bd578a36ca396032144b25717e18ff51c519ff9490b193117516b299be +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.xz=60f5672d8759ccf632e072cce922dd9d079a8e2325098b278afc4fa829943a2f +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.gz=bfd63a92d85035bdd4b4d445427c68483a34b41f647b09390de2ac9342ad6b54 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.xz=9e9b941612bb29e317e4dd10ced4cdaec502526fa964b3aa29cd4fc2af070451 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=2dc9ed6bbb9fd1d576bc78321a78f7997928a6a94625d90a613286e1a5bec758 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=ecb686e8f201952f1e3475a88de73eb264a11c74961967bd1ba7eee87a248bfb +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.gz=b481a6f88b8696b86160541083e608ef811f1ec0d9b1455325fd48efdaca6f01 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.xz=584d7a1cd7be4af3c747269de3466a312e541dfa17b1614ad355b32b74ea1ad8 +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.gz=c91990c99c5687216320bb82e9e2f8f7b2242fafd534cbb0ae88a287880c2dab +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.xz=c3a4a168393d0a8222b8ebdb6b5495a8896788156af4c454e35d349d1ab63109 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=5a7c5b256b41a9ee01387da8ffaa49a3914737c827113beba081f34671568e84 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=d0343608f884db1579847539eb6346ad3cb7b85072bf698598db61dee720793d +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=6cf78705518f865a7c6d3c9c4566b0dca6197338d5d88dc0037c721261e5dc3c +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=c8c81ffc20538dea3050a7b7ba89cab620423e66a10931befba354e2c4572ea1 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=966f512ab3d23e303f7162d03bc619aff54b0af25bab86bedc0c96e130c89545 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=fba9f964d8fb51753435c300319870a3b4530e385887a16d31975597cfde3ca7 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=997d422eedd7b6adfd175ef6a13b5ae7a83530d633f834e7ca0d94851ed29ed6 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=5ff49e69e9b549494542c9306c1fc82f6221b8d9e9fe25ec5021fe46f7c8cb28 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=2181081deaa27438f420df53847b8108f96ad215369cae81947f0688b1906cc2 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=f8b28aeeae9973c1dddbea684eec9a6a136fda3f850b4ffaa10e53914e1037e0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=a49eeccef00269063a10b1e623a54b0b500afbc2dd8e1db4ff8ae4a6844de943 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=32fa668c9d3d703adbf6910ef358dbcab344d37fd3ce4e81ba43a820d8a8792e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.gz=95c6a6a65ede296e8b4183a1271dd6cb1c61299f3966a8e2a6180867752a2988 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.xz=a44fa8ccbc73899c013526ca32866f63e1165e7f3b14455b91b37e3865c9e1c0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=31448ceb30ff86b5d8b4e7fa8fe5871665115d561e6377f73e164d8f11652b6e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=bd71a90489b9ed6134d0fd05fd22c58b20c271422628a552d04bbc2bfd1bd29e +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.gz=0cc22881954c661d2ae01058ca04db10912289812e9cfdbca3c066227e38566a +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.xz=439e6cda62ca17f124ed788ed8c3882c2999362067214ed911aa362b8d4d22b8 +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.gz=c62e75f3754b04b3594189de953ca6a8703679b29e8da3935ee5a7370ee0e03c +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.xz=dd23a5df6353aa01271f0d1206f927dc0f532203fbf66258b2771155e543b86f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=2cf5fee1c6cc0e2ee099c8687126b11861e12b098dd4ac99397a2ad026596350 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=d3a946d098ca9991b4dbdc4ba3b53e37535e62815f76f81bdf7e4562f3534943 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=2eed53b995d0ac0c2856c39815ea08f40c074165c3814336af591adcd9f8b571 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=3848bae0dfc660d40486ee56589fd0d6ee558f051a14b5efd9d55cd03d10f709 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=b40192daf614574f7ef2f7785f6b66d99c0876f3f3cdc30990f8545d41ba0fba +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=9e23ebe3062f38791cd23cfa58d9fbd796553bceff7a5a041d3cb78278915c99 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=1b47d8f88a4496a8c7604aa2b6b0a2a810266825c385df5e29d313d5635eab6f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=613dba102c8e8aed939ce3f1c43382d3d5d74a1baf911b7f68867ce0abaddbf6 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=6afea3865efe25d2675c6e988c09c1fdfc052b27d996f893d9e5fe8a5aee10c3 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=285ce1584fca2d0701baffb3228801c88212c20df1a6ee76bcc6e8a392de8739 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.gz=8bee2bdb49b1b8781b016b0b1032663e20d135d23b7d9de898e7dee0732a8ce2 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.xz=d0e2b1d1a095bb02866048e6286fcf762cffb69f437031ba3e7f330ec1ddaf07 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.gz=c9a13d7fe85291c560786835a0531a407459f07cbfe4b652d9497a581e4bff09 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.xz=be5e9534e88732ca700315d8a4517de3701201d74a0344e962faa9e9c64cf035 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=9a48d8e2247f3f70c85e8a5fd39c9f1302d56aa94fbac6b431bff5a4c74f64ad +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=5e210bb961e6f73869671298ae1172a3a6f61b1b4ab08baad81e0e32e0a25245 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=fc842d5db2778c0f3fff7602e8056795a0c33880a29ba0d29941fec2d2174d8f +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=cf473390797976ca153701dcd3ee41a4c2ba314cb180a06ee51e242f5f93047f +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.gz=2cf80485cb303b44d48f6555ca33ea25fde6daa7f5fc9e6a2acdc23697c810a2 +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.xz=625e57d27f5b4bc198598df7b2ae6ac8416951060eda7dd08ef9d319e97b2b9c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=170d6999f1f9185d48df161023e527826ad72019c37ee11a4e3ad25fa1667e81 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=724bf705fdfd23aa4065ec9128d6d34beaa8285b02f36c2a68d83b1f537c5186 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=21a6727b7bee42a9ed22167600a5c9e8346db3e10133c88a07acca89a3513140 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f6234d0030296bb13cd8b075a6b2637b5f8c1c9454ff764a168755686fff4b5c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=09479a598b1fe96eead29bc72f007a130b374b53d06be22282ad94ef7b4e4bce +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=7e67daa06adad6878ea5d3cd9626baf370188fde3e14b2459347665118173c5f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=92d63ffb8ef0c274ff6138032e0df849b7d10668035ec3a20304ab31bafad495 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=1c2e84999fb5282b6ed7bb31bac0f4c979483696453f3c797dce2e52b7bb848f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=3a5e0748561fb981acdcfdc2037b12cdfb51d4fd3336172f6be74a78eeaca6b3 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=9bd198313fe8e4d0d3b4f999841ad6310884f6a0ba9f1582e072108e38fbda0c +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.gz=45cdec4bacb8c1cb67e73c26499fa789fffebc981a33472f6b84d8752554e7c9 +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.xz=5222471cb625865c8a2594e88a3adbf529a866164e98f77a315953d5e3494c54 +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.gz=32159e59a8c11c032ab900f77ab7b25827409be390c62bfee8e28d9cd733c13d +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.xz=45c7ba589c6f092ada1a0acc0a107efb09b3baf0549846919ac3f853b574026e +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.gz=4263a0a290f88e9b5c09c6140aaa02fcc7bc455439070f6d4b4d9a38f8424c3f +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.xz=a3ff2f3691f7f62f9bb57259eb4e6c2976276fabc46e9ac6a7796ce2fd6ece5c +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.gz=d073f07548e5a2d92e094c69b6a7b59cf91309ebb42e06393c410c95fa90b681 +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.xz=e8708e2a341358c14c3259a8c4c05db6eb7baf09050cdab383b44f0b70dea7b7 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.gz=9072d9bf10db42db4e150b73fddf1085495979a18f59cabd43d73c0f1336e112 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.xz=e43a369c7954f4645243990291c88dae3b6694e4d2efe7ed4f81b7b8b7c723c8 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.gz=e7de687f826c37e20b64b5e091fc3ea514770d38c45bb39eb6ab5a9730be6d11 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.xz=5ab4c606c30f534fa75351160af4c4dda660955658a0d6864d28d927b9ca07cd +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.gz=5936848fc7f45feeb797c6a78889762d0b36fe48dd82eb437049be6e8d17ee04 +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.xz=d66a746f476ad27525c9f22136b664c0a98a811450caa083ff8c9def80dd886f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.gz=d6326c139001357e964fcad7e26b195fb707e8158791fdfbcd16910258438910 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.xz=f455268ea0f33118607adb1a7ebe484a38904e1c698899f4857492633fad219f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=714846d27f13247f37edc266b66eb1792fa1c35d2dd13215800db55ed89cf8c3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=cede0b7977ee57ed24d792749dbfb42240f95a235d93b2e1a7ad4aa03d588da3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.gz=70fbea3fa87e1aa93668f2462ddf73b05c55883ae9d2bbdd0a3acc9ab51eb961 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.xz=bc13da70c924372a2247d125bdfc850b9551e32ef990ca8dd0c24d282f9c7025 +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.gz=c4583d916fdf07e7e274b59c32fe661b0c1780b925c4d84d01fccb1262cee70a +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.xz=77612c690f5ecbadb1f2d9128f79676bc3b199478cdb619321eb521e4f8777ca +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.gz=c487e6e164f31a9274a24a1668170db22e64def6412a38a38b6e0c4741523786 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.xz=cfd25efcec9004d223fb7257a34c0530ff755eb3337fe80468949df185e1adec +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.gz=5e208e822d9d2db30bfa4ca363d29d923c5560ac7d220dfd32898524022aa2c7 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.xz=742f8c4dde07be899de43502af87ea1457a4706078ea7f353914fd31d9b91537 +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.gz=3567303608260def6590e9f0c29ce776ce933f0a9cf4c568faa8713935ee9efc +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.xz=dbea586494aeec9d2d29c80eb259cb6d66608c93b26f560f2276b490d6946a25 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=ecbacd29dc59c59ac513958325ce3a2a2323156562dbf966f529f267fc52ce17 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=69c2429bc0bdcb3e4ab9b05af37f65c60b936df6598d375843e63b8141371fe3 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=f4f33e673f883a9ac3b0113281c890839498d70d72e5ba51094c851bd6f0d323 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=c82917544801fe373b007aae871b4833db428a37d544e09644601d3fde682018 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.gz=d3fb635af8745de8a5019bb9bbc3b20b109ed85f7ced6e939836bc0a124a0fbd +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.xz=6e7e2d66e1c99507b1b515178d6a200532e1ae7600dc125d26b8ccf52d011399 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=0fb8285f64d616d741a32ae07f69dfa96e0d231cb835a839dc42ea76fdf0ea83 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=c1c92ca9c3153ea5f42aeec0c3ee7f1042e3e01d6a91784e6baaf201c9621608 +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=1749eb869811470620cf5e432b5f5dc128c6b8e9f3b9586fc20336c677b863da +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=72c7a3c88afab27d0e4b09c313af336e6aff45046fae2becb17235affc1746f6 +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=36bd0cb97b921cdb915337ec1c7a23609c94c2485f932a1c27fd71804def533d +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=45a6098b27a0de50e3c26d540ab4afeac30178bdd389c0a4d829b015cc5726ec +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=00352c0a8fba406add478aa1bbf6b6ca99e7c5db302901616bd2cc740a9867b8 +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=69d7ca4226590875ae59a45b45e7726780dcf4091c9c44c3759931e2b05a42af +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=6243b4efcf2435cf586248ebc9b99379e8b86ce5575d8845821a11c21f8946fd +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=78898b9d99a25075a8c47e7b78e35b64b891179a8f54d41983370814f33b6eca +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=ab293f2e7e3288a4c4842ee510307f18c9df6618702695add978bd1d56be21b1 +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=57b7c326e4167d01385f9f89d89e81b5b660a7e63e1c17b14b70e8c8e50774dd +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=167fca173e6e2974a35be4c8fc5cc9043a3b34d74efba13a86df75d2600576d5 +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=4197393d32094f65fc781bf39ba95fb36988508f9ab6b41300ebe2ebad838640 +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=d3d0569bffb348bc580f3e382557939a4a87452a05dcfe1fbca8c8a9888c457d +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=c0ad9def2f73a60b674b35b8f53483a0c017ad4e4db3b268b3c10bdbedc13625 +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=7f41fade9abdf4ee47e96ffcf345fc37231d60a645ce814b19738689018b77cd +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=d6f21bf60530c691a6d40d0ba1ebe0ee041bf8a333f489671b1f1fea61950ffc +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=0ddc2427ea4e448789b8053f111f7310f6da95d6cff8f644ce1f3ccffeb16f42 +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=77a2f8531ec6a87ef95ed5690c31d5e1e3cf65a633e0ff885cb0b2cc55a2fab4 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=c72a5c97347dbf43c328170cd1ded9363f1f3659732d9497d73bc2a9e3b32de7 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=584e99c8fdfd6e186509ad41f67d02ad92cd9d887c2b6159ad8a36334eba7aae +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=536f1135a697180ed7674b8d9ac15232eac4d7735cbcc836cc099cd6136c39b2 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=dad89574543674fd72dd2a6be4b2753b4e2211238d6208b201903c0645024fa5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=1fb747dc33e3016ec5f0d8161dadef20f0798f7a65967c316f0315826bf7e8d0 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=05c57bfecf04cfb5279a3132138c9bf0c6773ea5fa01315bdcc83f0ad06165c5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=83e04e9c3ca61c2aa39a0c37b92d11a8571792244e6c5ebc795c1c3274a9a926 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=4c1537651b695764db9ae3c536a605a7a9a1fdf51689350f7fff9638454844d3 +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=1c3164278134db89dc7cc5d2169402f28757e55c99438cb86a90cbf06f07ef7b +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=ddb7ffbad2e308d80b901986f6e00f38ecc59359a9f7d28223c089b01ae86f91 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=935d864e0e212ed9f70027fa7f6afe18757e561347c4d8b97221db2936a5afe7 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=18cc0e5b000474a60938521e23c5ef057e184616ddb1e2b031db0f8b5d87931b +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=e824c24bb3933941658a3a6c981e469c153aefc5bdb153216d90cbe8dc26adf8 +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=d40178695aff134127268c21a91ae8b4aaedf2d78122a00c844e0c4efe246815 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.gz=c37a91e3c6685a11bc96acb1f7db48c30a45ed19c9a7f303ad4b60bb24228975 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.xz=cf190f95859df6eb65ca3cc07aee95d4f3f8c0770c480ecd8373168cf0bce323 +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.gz=b598d8aba2ac570320ae7a1c8b9103f04cd0b81df37500ed838b88874eb7317c +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.xz=ebd4c1ca68d4ca5944a3f0837a8a1018e18dcb31ef8436a4d02c347b251ff0be +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.gz=3d0385fdb184a66a1af91e3db28ea0829b1a498f2431a896aeccf2c900cb9624 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.xz=f778bfbb7033a53c61c3af187ee35e9e7713e9e98a6994f11ed9b589f3459391 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.gz=0a1669fc57856b2e475a9b8edd7575ad7ab07ad0b95a0ba9fb92e7fb26f81a0b +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.xz=bb6b245b183061f65e3a5d9e9c1bafc76c3f968ed7d0cbd0e33ff878f5e30804 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.gz=3f459b3a6dbe99fa6bcce86e853096902f23e7e49a1b8c300ef4515796619b28 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.xz=b9ce85bf7cfcb13526c2cbf56aba726919b818168c6f99fb855cf00ef8d0c76c +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=366797feb0b31dccd500310903b494682b654800938a79c46e47313accaf622a +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=33c50d91b1f23770031a3e1c9b3a78f901b30831bbed8c7f463c29ebb86d5a91 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=43acc164b41da62e1aeb7f252f6d81f1f3b79281362348817134760185648071 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=c93bc0681027e879719417049e61ddf7f4092d72d103d14994520bccaf6ede19 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=0b36bef54f41302c871bfb92f45f248c34bd2450b159c68801eb13373d464e69 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=7f9300e17e4786747ca434c1f1a0451aa7c7f933f7952b5010a0e4bf4d9e4198 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=ca421607c9f67d0648dc7550781bf90983dca6f17e81153ebcc4ae2f69fe88a5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=0b20c32941914bacc1b0a16b334cd32feed1ff56e6a2551aa0ae6b22c41f4c46 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=e792938012865b5c1fcec63bc25a9b72e28889515472cfd83d7d57fd8f84d7c5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=15338fd8f01e0fb422336ba055a4ccf4be3ff8ccdb123ace84e9dc9f1a64e3cc +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.gz=c57a71f14db1b122dec0ff70a04faf1b8a7a3af948e3ed81b15563b19c376334 +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.xz=dbbd2251d206de84de81abd5b29e4ed1b08c00155141e08a7b6fa35559ac4e65 +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.gz=852381932ef65b7ce75f943164ac747bcce3b1d3f6e2c1cd434934c1030f789a +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.xz=cbdf58a9afb016aee98416924578d0a8ece8a6d5a17734606928bedf37719129 +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.gz=6dd459e217ea3b4f7f85811eca500c774a36dd849addd9874e4e54e972fc7c7c +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.xz=beca27838a50a2da0341d1bee58faaab18d67a41cd1724d39b30b4f8daf18359 +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.gz=8a264acc2656f35e61d4ebf8773dd3b29c0e7748ccaf326898089838128c609d +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.xz=eac3340c629af165b67982326cbff6cd63a2721e41972b70826a9911af902e02 +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.gz=ddef01fac3d8b1116b0f2b5f7b4a65a54070e06ccff8fc0429ed8e668d57d4fe +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.xz=6de7754db16d34a556264c6d37ed66480e832aacf2aefe34a7e82636ccd38653 +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.gz=8f8eef02573a4911630ceb59534bb91a084ecc9d8ba1ccfa6e59370f69af028f +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.xz=86bb30d39659171af2120a86668f1803f3c1ca7b2a0f8662fc98a499734f9e17 +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.gz=b4e018a5dec5b232867770a8d8ce43d670265a51b389c7a1f0f1652a9e2b685e +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.xz=b81c60d6b6c5f5fb1eba3037c545452bf7f070eae7b6c23ab3011f50de1f01ce +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.gz=61a88f5fd29b413ae351db90a43773ad2cc846ef3a03b5747e98bcab7865b988 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.xz=977ad619301f051e241f898c302bc230ed37dcd6041d925378cba6811a15f41a +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=184c6fb1f051685f23a04ee2a57d15b8f1f3d5696b9cee0f8d48b230f108f408 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=90715fa792f08da57964abae2b8366337c3372ecb92624783448ee6fc921792e +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=13343f16a12d12bf5dea13e240a88e50855a3eaa16d87842e33356611ef6a0dc +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=f273923ea18276e3440003bfe6f00479df907dffd18fb80b447c41baae396cb7 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.gz=3c86701e32a065bd9b0529360379b86180724497b5167f776253abba208db741 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.xz=daf93cde1b13402a2e7ef7faa2aa1644cacb2fafb211a28d4e1c7cb96e738372 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.gz=5cf8d53338a4c504dfb96dcbef5b2862921624c2c93edc7597478f97ef3f1a24 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.xz=a8a4105596906d4b1ac1588c1b301c1843c4ea695219fa6a979205a6b5fb336f +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=f9c03150ce779c7a31ccf5fa4e12953af8d182bd7c0415300ea0ddee191a0e47 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=fb81f14fc43c8c9e86da1f10dbff6474447029781b716c9ad67068f793f07ba5 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=de329a0e32d0f66d50da01875c85ac807b826d2d2efbf971a7b46f89710dd2f7 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=ea5ad2523161e086b0b898c8c989fc1b20a26b460f4403eeaa9fde881be58465 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=b042f515a81b7151a0930e852fcb23632b74579ddd8be53dc37bf09fa94747f4 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=6f850810bcb1ab381e0245ce7d7881d86852d041d679d622dab4f4317aca54db +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.gz=e415f16fbe3bfd0d9283ee41dc385f3a1a79f33730aba9d04da2acf7ea993617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ce6967397e9dcf20c700957d5ef3a8fbe35795f0b7be1e8f2fc61ebebdd9771d +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=6233e4e853dcbfe5dab0b1258fdadeec39f3d343fd99e597956677a949de1945 +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=228a0ccf377f633b450b6bdaec99f351cc9db41e61d3caeba85f675bc9e07954 +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.gz=d729b6e0f6d70104c730e7cb4d4472ce02452acceecabf98e411b2ad0d6cfdfc +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.xz=51dad4bf611ab3bef919a9961f1a4d02f0203c63db501e32ff0810b2a1d81617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=6c859df64c707c7783863a17e2980c2139a98293473363efa6873d6549b52e3f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=f4cce8d15f242168ba0ec7942a1140b1dadcaeebc8c0e7e7bd35ef7f91e4b390 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=f7a559f89b5fa340c2974b8c85aad21e48995af01add8e459b82bae890f0b2e5 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=73007a7a396864a71003de912ceb847e8684c4c63d45f032da628267aa28cb7f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=7746927bd5283c05717e006d252bba797b48bb9f50b3674587025125e1916ff7 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=4d2cc7aaa081dcc0b9fbf09601881023b6749d8b2986a01f82caae0218d2cdc9 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=6f2ecafe9003c8fba1f43e1acdd477cf0482e7f97c1687d6255c696bc45cf662 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=c82ece042d0980b1707d3871aa35b4b816179c3214e84ca7666e2189e4f062cc +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.gz=d41dcec02cd3dc2536786aa461256bf5ce1b65174ab6cc532564dbfb6dbb37bb +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.xz=cfc7dd1eaf1420db8b87d13bd9a857e258c2db4137ce1fab03583b82cff5e3b0 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.gz=234209f51b8e8637cb2742cd482be0a333e4463869abbd5f00e548f03eb79483 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.xz=36a5a189997d1c6eb05397007223611fc9777cee6fc495a885e46c2f52f62040 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.gz=a8df20db51fd121a970d6dcd24e583ac831c956f37b7e47b1533cba6f4b27dd4 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.xz=d1368bfa84bae9366cde19cb201fd13fe09d2c0b53f82cabefa85a78acf423e2 +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.gz=625834a177f20b99f706b9b25c60388db6cc42de7dd54a9ae10b16a86f4ab9bd +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.xz=89a95f2c6a11a0788fcb4fb12c7b37dcae62ff682ae8e1d24910f64dcf801be6 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.gz=be3bbcf4c95c1d30634cdd87efaf0ea57b8199649a9e502245ee3915f93f49c5 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.xz=74eb6c112313e7bc0104e211397ef998c2713f3c350a0b8995355ea04f4c1df6 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.gz=089863e43456e8c2613cf63f82172c9b2b220d582d448a88a9301f12aa5471e2 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.xz=fe604aaf5d859192fa13c78643c072d49b31f1b16fdbb088e2917631aa683431 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=f4c710be8d286696c57bce005e43458a5031fec2b706a8e825c498e7b1c9faa1 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=18e510f07b0a6b94cc1ec14503c7a2c992ce17c9897c472d9330cf55514ca4c2 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b2a4e280c9150683d14ab98770a4bb6f27c74144abcb0d075e81e537cbd2e2d0 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.xz=096d0926b219a8dbb6952a66ff742936c7307baa236262b2d6ee5238c2b44153 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=58eb3f62b876ee4be22ca51720204da9c3734a5a4a5365dedcede0a012a48891 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=1ca32ac5479c6d27191c7726b0dad38af34e6423ba8e644461ca30c82382ce06 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=45895027f54b7c4f1222196317ac1cc38a84ccf07696a72fb91b2d42218d4281 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=f08639a9a86802637b08df47815e2b0e32ce826f76c7187a140f97abf1eaebf5 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=63dfd33bb41426e81a8be487580656699502396350cd6c50a3bf32cacf2f5eb6 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=1033972fe7345a4cb84d0ba8a479159bdda53d42f581a7087eb517144026e870 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.gz=2f00a1e75d995f89ad2b5fd509dcbce97aad2096638cea05d0e9662fe8f21997 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.xz=535e4ea02ffcb59ab5c492689aa25aaa7dd026a9db7e5a5e1a674464488e86fb +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.gz=e2c01590495c6c2384afb0287104bb9e9d8ba555c100c18c8bfd7f06bb4daf43 +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.xz=6ace1bb6fb5cc03358433476d31f25076eb8d963d685df9c8dfc389a9caad479 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.gz=a50b87367e59dcc3a1c22101d69cbf45a1485e42b2e12b6211d0f35059698d36 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.xz=76d25cafff7f67abe142ba7183b9e1a1fcc18bbfe6969ea03783fd1cbdc7cd68 +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=3bd98bbaf3c1f718e4f40aea4dc0ad47e6c33b46b4b169841497b49a582b7caa +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=62e384bb07173661c6c1e6ddad3d0fab31a25184966020f70b8492c916cf852b +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=6bd1ed4631ae5020d4529fc404ef4cf6bf4a93616fd15e1cad9527277fbe200f +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e27425b6e8f6c5114aedce100068bda25be975d80761d6478c49588c45f3611a +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=2b14ae8e9f383d2682292ce26bb5dbe9dab69b450564a5e3f7db3fb93864a21c +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=dcb68fdece4a4a365a95c440de9bd360f8c1079f4f15eee3d61a53d83957c7e3 +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=9e9c94abd8fcdea831451105bc351bd9407f3ab4925226a171cb0d170a4a697b +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=4cdd8795488a4c6ffcf9bc446576191dcc364e58dee785bf3ac2e756e3a9d5fa +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=4eb0a9d2a1b85326c393250873a4550c8e6617e56df9a2d313aa220ffcb7a9ca +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=2cf68732cc6926ae9727f627e64fab12c9fbe8803a640b25697ff6af23dbd8ab +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=4f276075cf0ce993ac88728992369d996dcbe4e1f68e5e80e620c0a6b305a484 +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=5b52acb5500d93fdccc9bdc03e6e91313deff0a2801c2b8a587681239b2a6ecf +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=8325fda118d5da0b376c0db751cf2ceeae2dd3021b930e3b55c14e02d0ac438b +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=2a764695907dcef6b4259831636d0020e72dde5d213f532b3f04b0bae3615d4f +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.gz=12045986bfc2c346a914bd0b18d84d3e1c3103d6082b08659aa2508a483e0817 +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.xz=ed09f5e3c37582c4ab42f0b4ef1353ff29182579818be28d8a184b33bec728f2 +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.gz=d026df870421796b76749529fd0a7965d83c2d915fa42599613c7521f92e391f +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.xz=b2174c6efe4336f76d30f55cf83dd5604b2a4f706a76be1cc57780a45d5fe1dd +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.gz=7047bc8731c2eb00043f0237a12f5261942a1b6b99d574cbb6db32f2f1930180 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.xz=578f2d6d433dc249fe923edd0519f3db953c0a9beb8a2f2641e1bdb65863526b +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.gz=0b4dadaece478e1c0cadbb479bd2acffda3254f2ca3c2529e3b81a0357019239 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.xz=19dc7572f4ae2a9b46c9017f78c5ff7108e9e293f21a3a52eba03f29dd204f62 +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.gz=71672eeeedc18240a33ea8b0ee0ce2ca8fdb020d5701cf4b8d3cc464a0199cdd +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.xz=e853f0e42602ff80f64fe245863ba0a4f50d8197ab4cde56c4bed53f4d7d9cbb +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.gz=42c1b82bfbccc05f381376b59c92d453810ea57d85e206b9ad958d0e9faa480d +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.xz=b83b7cfb56b087a0dc648eab5f695104a1f220896a2981d2b65f67aa1584838b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=0b87cfd9dd3349b23fdcedad9a0fca316ac59e720c4c8f7b67c44020dcaa9665 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=19883a7bde6ed9a4587469f0c7bacb434b9734eb74c4b9c570fd3c217db6a52b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.gz=11934bf3879a9c395676b9fb737fc9e6e0b54314e51008875a72a80d8e773a74 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.xz=2ccf643a305060f2fcd24b957a8f9db28dc6fbe44d1b947007a571cea976d973 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.gz=f9982255fb124e74a79708e9e566fd3713b904919260ea498279cde580c9b899 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.xz=18bd7da3c593a0bdcb14140ce19e5a43d42e041fb897fa4acf12f35e03b2d766 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.gz=511e0a0b6bf59a8e9ff299a32e46586ced5f13a1ad43c9564921c27b905457c3 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.xz=dd75a377e5a28aff5eca49c73b0b52a2141f347fbb3d2c164d87122663c60ed7 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.gz=3097ed66faa8489f2fac32453d2ebeb7bc39d5d58b2fa60d47e5f539a57d3658 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.xz=dd26d47db7f35a41eb16829b35079d2137eb41a254c810e8a9e6d393a899ed96 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=a3bdd2413e3d040a1063113d6ba868ed4ffad157f97f180ce6410dc07d89fec4 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=308402abb63ae6e364d4ef014aa9dcf338b86982a25fc0570c4ca0e348b50bbe +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.gz=07a3f0f0f6eca797d9feb6b3339a7c87ac4063bd4c898cb49d3a61c43b7b8ee6 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.xz=9b2fbae0ec014d61a3d20e532b526fb92bde03ab154b0ee02cb5aa4071c319cb +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=9d6c781e2414d9ba43648fa958b2cee3937fa62d118ffbd7ae11fd71ca797011 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=73d6d532acdcd6c64f48d12a24ee4abb6f46c6dbfc3d5c3d593887e98313c1a5 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=0d698e42477ce7c068df67b389e8f63f82649e1e2b38324e3c3b8906ee9f056d +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=9b94df3a1bcf891595a8263a61e3aa82a66013a5c59e8d8ac7742e033a1ac590 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=c010c51bd0665e4fc8a3d0bc27af75d2bd112dc8cbb841252b3d44b3789665f8 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=c7f9ecb195ded5ac7a03d353e51c264e5ccb4a3460733b06fdf967d8e3a5f085 +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.gz=5f4ccf0aaeff0eb0970796ea23aae4590285b42b62b165fe3898649e203f57da +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.xz=4400d650806cbdb2a8e8e2d6ed0bd5f90fa46b8f2398f09fed98ef565135635f +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.gz=062eb313a72787fe53435c57b8a5b0c16ad2abd0644d8669038090d3572bc94d +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.xz=ae493ceb9e334b05345cd94a26b87f317909ee82c02f704c2c3b487f5b28e897 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.gz=0d7ba60792aa6e5cd531362ab92977ae3368b2ca2284174019133c2ed9d495a7 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.xz=ded4113933755736c3248f1b0e26cc42d436eb4c6cbb21745f87a2428eae6ae9 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a4525272c4d46ab120d61855d5e453b92b0fa83623c8c5cdf4384b746ba56925 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=5acf5b947a7d7f546cc405975ce5eb1f7e1a86ffaa6ea7c18f5baccbe8e26a7c +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=628a86af3590cf6ec46173bfd18790958618e1236d3bb54f6eb92be7f78cbdc5 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=970c1e229fe35bbeb251e6e1291618d9da5334c1608685d8d602209bccafb4e8 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=25ecca37c54464052f44b79ae061c992f9bb29a671881f5fdc7a8f4e1953e543 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=e353972b9c73228c523077137a000d1c265dd24012fd1f3f44cb16ab172561c2 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=b6d050a3cde8ff3c94dd072fce770ddf43486c8db37ca2ce2096f427f169cb81 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=1728393681aa26afdd9b4c6147b1cd95a43e614cb379fd2f0e38fd68b26cd1fe +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=4136945a33e3658464835c0c31a244b953b1bc2614dd38266f650ee8e330d5fc +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=e121b45e188df1d540bd3d3d691b0b4043d5d067b52286e3e82c544d1f059a38 +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=6323723980ac6c17e951272bf5473b0f3735d05f00a469317d0371db9f1b562f +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=9f7120352e4b0e147159df5e04c1d1939dfe0e1081a6a0d8915ab75709147079 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=32f52736c78922bb47a624365f3bd7a06d28581ef4f1fffcb57575b6de739431 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=50c2b4e8c57bd0f4aed56d88e1bfbab2fc8f6c6e6d2aa8cf83dbb36067f2ca69 +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.gz=5c2082cb9fa73e0af8b3384a3caf76c7dccbe7a78f553a5527b3539bbee83c0b +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.xz=ca8c580219fe697b9dae674edb2a2c6ea6c737ebfc4d2580cb184745625874c0 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.gz=83ac808792dadc4e5779c0b2185514de339cb61359b185d714713ab366909de4 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.xz=c6a92d2b255736788cd53cb99b84e20ff135c4e5a7ed9cb2ccc9e5eb37523239 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6984f5b15e2f56e12924cb251d99f7b52307b1368c47ddc6ec4d17bbce8959e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fecd2eb58e8e1c9422e4433f120c3456f9cbd0f216863d9e3d37129bda966f79 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.gz=674115dee679648c45203b9014ce2aaf3812b3de079fc12aab4ba34aa4d8e8e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.xz=892ca2c9bb499ab0922919d89bba3c176e75570be2f4f2a1ac08608ba490d4cb +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.gz=513b3402e525d1e28c648a640d4506f9456c5c2fb45e2b7bc7f2c325a2d2f920 +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.xz=5e5a6c353aa3c8cfaafa694e70b69ed54a8c8dea5ea46412fd191a28be3bd926 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.gz=f16841404d8bb2aa1bd530eb6c2850e665c9e90f82eb6968cfcd4063b5ff8d10 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.xz=3c23e2df43a58499b4cf3652b2b02b1f68141b417829eb06d44c977c745fc7ff +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=43bda68cf861a73c8e9bb461e4c28d3c5315f04900db68168584db4f85e50492 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=0d3bac2b017cb529d3fcf81042edae79362842f191a6759441afab41b19cd0eb +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.gz=af7613a78ea98f2f134a69a32bdc09a103f83d6b1ea2ea3c27ddb3eb74430695 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.xz=78baca17912ad3cf49214187471ddcca890e787dc9821daf37103ea9f8632c1a +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.gz=f85ce010c7d235358e532b23e85ca8fd287376c311ca9427bd41ca0b3f04510b +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.xz=aacc604a5b53267a8eb936a433eba97ad4a002558836c933548152fc6cf50b0f +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.gz=1ad6d3a92bfd2398af3f712b8238014b5517c80c54e2334e6bdf5bfec349bf0e +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.xz=fcee1911fad43cb5689786b9603ca43fe2e6fccc89aec1f63b8afba4df915a71 +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3d219a1e888acdccaf75afc7c7820f45eb2161a0c78304f76a28af95c5a53dde +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=0b4ea270256e8efb6df7d60473db256455fe7d5de835f164bdd0a92c3cea7369 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=a7edc1692f4a5975fa5877e1af63c8e9c0a2d0946086413678973990fda8ee50 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=bd4b98f560d00da97f2e7899ca32729581f9c5a386f398cf477c1ba6c922029e +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=49387f42c45fcb21d55e056843ec3ea210c89b10722adfd2ad6ee5d4a929f7c8 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=778b995192beced6c690d80103c719e4b30c16b5e3caa2e2586693baac7ead18 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=906191d5b2ca8e7645d6ff8571419aa8b50ca62fe10a1934722f1a493230da83 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=6944b61661c3b1f72ec86a80b222bdc0a20e88a4e2a2f342bbb7ae2f8a1e1a6b +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=e7fd3ba048b6d20956f6a76b6e859204bb9847bbc1305482426bb40f5b28ad36 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=312042e89eb0007fccdc5effa6098a762b94ff3a15840dee0fe681486b0ebda6 +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=584de19a62f85c418d01bc7943f0e3793f4c79776b8aeb5a7993cd1e7141bd7f +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=43327de3051396e27793390468003d1f13da0f0ed1cb7d5224ededf2787fefe8 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bbb72a900c77cebec9cae4b2c6fd0e3bce3451bb2fb65fabfb9f042a55163f78 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=fbf8acb567141328ca628bd3c81af9477eb177bb398be3feef55d57acde2af5b +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=4a80751fdd514a71d20d440260a86510c83cadbeac3bf03014ef5377c6530e10 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=fa3d56f5ec9f88448a2b6c36809b6c21bacb2cec7350571d386320aae2b410e5 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=0f11501ddf7be92b987404df21fb4ed83ec8893d887943d86031709e7d671900 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=264accec3e4ff18a3f21508bdc22988c86c0c899be83ed491f7f45f4d8d1f76e +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=a32c70abfec103c12119a5adceb83eae81b7fc950733e6039e0b3d3a589d16c5 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=b0ed3c98ee0cabe8e85d031539796f0ff3e6fb3ed90275242d6fe9f55900abed +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=b4f8456e420eea3025f32303cda5de26c67d061e04fb2229a247411ac7dd8351 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=e66969acdf2d98ffd854f19e7d9f8beee8ec8fe7ed4493bd308b2f9b44cd9c90 +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=f97621947a0a90815867d7a0a08722f87cab26584a4b61dca7d81e50336b16cd +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=9af1743797ac19982892046c5d4d1865c0fa1e7cc0a38ef43f2364e215a2b5ad +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=fdf25dcd2c911c4fc6fbb3a184d078e36cd908a6a7b135c2a48a6f6efabcfd78 +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=6df2621585eb4d0c5be9b3118034b70dc12e3ac999a47e843b9b2c8ca48629c8 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=bd6c79723c219e2c2b56bea90e3c2808bdf2119d299fb84d11d021895292d464 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=4e1f249176e480b12ea11bb49377cda875189f382086339ed3ce591e3c7576e1 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=36877f682d6c43aaa755b5edb5599e9ee8f48969e93532f458f516ac052dd47b +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=ef36ffefbe132c56c3a993ba4f8b9048f65ac87f3f22e9471064d3fa7c0e9bbd +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=2d9a0f3c007b59fb107c859ee4eab0ceadfe4c220ba45bc8c8f4bf5cb2608ba7 +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f09cde2c36a4b9791f1492b0319ef1444418b2162215d48d18a6eb7493dd275a +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=585ef59a9fcb414ddec4fb69a6c33548a38b62c87be3c707443954c7be9eb764 +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=4da9c4095b0e30d61c9617a5cbfd69ebc77d54f248dfa3c2f9c096383184a762 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.gz=28ed0f8745b5b68527ca735a794b11a1411574cb0756b74b7e5ad1f0652f1670 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.xz=6b3c264c1e1002c381bc952ae5f24f56f7bd623067ef0f0179677798539d0aa9 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=46b9ee6fe5c5bc6ec504b62fd9356cf467ea001cea073056fd8b53265b424940 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=d888eaa7a4b42b4761cba736c75c29d047e31d6d3918c3d64e8347236918f035 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=3b04474137945edf639d267a67712166c07df4c018276dfabbd7ad714c952750 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=f5268dc4d20eb86ae882c5faffdc1b6dfee52ab4487569a02f92e2e001a936ba +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=6fe48737c200f3ab1ebea3b5089403ad76a8dad4754cbdfa274c89dbf9b0ea45 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=04f1a8d21dc54470df735f6b1f09e9d6e26c106d4ae4405962aaed11da80bea6 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=0dfa280e767a16665aad2bb6051b88a84850ccc408a24d99b89bf8d659149833 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=601215d564ed36cdd9be9abfc3ba42bbc15fc75d306e0921b36083a293067980 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=ce5f4421a454ac2e5f8e53d0ab6050be45f73a3931b9e60240b13f7bf06800ee +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=7cda6e670d0718d13b89225f58df1c6a2473e6d2626144354c854d671c778ba2 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=26e3275bab7d39c465bab76074dd54f534980c89f5460838da9745c171859604 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=b33178fff841b91968c2f6fc108389a1740d865214729406de40b49e6969d22f +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=cc8439ce6628f864503c7ca322ec0607000536c002895dc872ad1bca8e865119 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=2d3c2f666bf57384e11f12dcb00346f73953ca8a2a60947ae5c0b14b856b9170 +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.gz=febc42bc953bf6b23253670756fb294545a1db22b02fbe2273e1030a78f0dc8a +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.xz=f978d9d9e0bcc75efd4250406b796fb3f6cae31ec41ca2a40f88f7e70201171c +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=6dfd42938c385aba18ba477e4430dd89e9b780d9217f410f68094d7bbbc722a3 +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d79b6f6158a99ea82314a99a68c52c63deb16d777d74426780df06c5110576a5 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=5dfaa19982b344ba8d3f59418a847a764eb8b6b4a9809b8a63aeafd4725cb406 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=dbe770d4594c9f8cd27aec8d342fd5f6bb44570723228726cbf19424dc0d7708 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=41357e54d974f3dfe0e71cac0eedc69625ddf7e2a1cf563c2e9689afa53cef11 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=208ffbee7b68121ddc0cb92dc1ab73dc3b489869ab190abd99174026724a3493 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=7970e126f177ea336cc243448d034c8ad1aa6489db36ea0bde54e2df472552df +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=8333efc4f3bf9d6fb01a947a6f631f03fd506a494060e426ee7621831dfc3a95 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=f8b9030db15e6004647772700a7b137fe9e7ff1c24fa0742c45a25d072e1f1e4 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=f554b976502cdc78735def853947d9b48ab1ff1aca208cb348efdedae0df407d +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=402109bd4811e723999c8747978452e2a96e2ddf1109393302f9c1be7e36348e +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=b2dd7f94bbc34b91fd2dc2c1bab7179a51265bc51ac58b66c8aedbe849103749 +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.gz=9e3f1aa453de7bfd9a15f1925d96b46ce6375b49c6656db14bd2ed99f4138e1c +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.xz=468a157752c69bb2096ffe91413086d04eef8c64b9cdc44b51d7e9a94d2f4b5e +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.gz=1a9977db5843941c49c808830364e32c7e57190d3bf4dcf74e2a109473f808c0 +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.xz=7cc68c40929538b7711f5bccfa964841094eed9859f490846de0db0b2ba74343 +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.gz=08c86c8984b966d3943de2252318aad2a27899fb58c5eb1079b8b8164b56d21f +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.xz=d3b53723f085887709951dae7b0389b8cd4159a22fc7189bf7d7bc15ecb4bc57 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=515db325eb12b211d08367436fd07f5ceff4743930b9e685b0b13b94b99bfa34 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b4986e7aca0325059689f8d7ead67616b2965922e77da33d9d03a7235520b585 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=541571e5deb51d360eeaf2c88a28cc25c98dead983a3b8f2a6e3f371df3ce0cb +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=44fa8d77bf945b1a5ea9ed2a7e8228c8c0d5770098bbd8687173dff58b7005a1 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=1ecae3935e253cc9b7aa44c8e7835fdf039d44ae19080e2316421b47453e3527 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=b512b895e9b78069e57fce8c1982ea6fb8e463fbfe626474a6399008d5c149cf +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=91c4ddebe0b50e8778297793d77a583b23b6c333d785564b97ea38a137c3b7df +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=562f81acf112aefc6878132d7cf352da64b0eb1803d8c371e6713e270bea15bc +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=147bcd50f63f068a5814200ffeb8adc775c65187fb9c178dae5468af3fc04611 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=9b6e863729599a96547119c1c2a04f23167cb142447644b88ccaa86d073e1471 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=85c085bf2771eb79dd3aca31ff9896de969de229c0549bd8c89a539393ef5cab +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=107bba3499f4446aec6d79919453341a0d10fa5e6001da4cc9d3f1a618871989 +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=ca6663608404909610479a33562049d313882ab8252fd302657598b0251746ad +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f66d3d9a1d897346945b254aed29e29b82be1255908dd0accc7deebd698a0b78 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=480fdbaad5a5e9d6412682c61de324ac37093f60377e5f772c59dab58e63e6a2 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=c438d14d90ed5acfa2108788b7179aebd7fcbac10703b2c5207ed7eaf988ddeb +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.gz=b91f15c087c859b8ea6a5d556835c296ff480f6927efa9e535b9dcd726c8c4de +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.xz=64b1129ba56b964ad433a499b2b5dd590371a4a7fa0f859c551bced786d876f4 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=c90d259076abbb62f26348a2bfaf5bc1dca1e6ba7d4c1dec8f02d9dfaa21cc53 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=127c2335d668cb93169681ec3775e0c9a91e0c5e1dd6043bd9236e18b7dedbf1 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=a0dd89c80a4c2c724a8910c33c6d5159beef9b462f912326c659defb87ebe41e +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=b2c1972028521d6cd643a7a1f4db2eca1da5eb8ea03187bab5a7bef63377eec4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.gz=3eb87ac0585b6a4a3cd0525e3f7fc04eafcb28b64ddb866beedbf24401810bf6 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.xz=fa93c78e53bc3e408c1b6d7917e643fb7f1f6203ec4a44036e8b73d37e650b82 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.gz=3017ba6134aaf6cae0b7980ef211420ae1c09d230fc082e9f68a5a1a7335aad2 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.xz=5a5881f7883eeb638c00ccc7a4cf453ddb987983228b066ec955b92d4e4855a7 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=9c7796ef52678a29ea7e0406c1f2273ee11d907b379b3616237df9a05505140e +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=0c978e79f9794221ebd7c0fc0d4313e323d1aef1a5358ffc9cb806fcd997e4ef +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=21ff96c7b7c0a5a18051c12f01b67e90fef620c887f7d91e78944b5a1a0e8838 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=2b7ffa8d546ed9b89472fd25ac56b4f6e968e99bfae9045a4019fab319716cf4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.gz=ea30e5cef50b7b207844bc30ceaee4bcd30cde86f636080b243eb3e7e5367042 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c07dc61336037f475cb74945c0925fabd1fb33412eb4e6eba4004f207db67f3e \ No newline at end of file diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 561b611148a..feec2a7444f 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -114,6 +114,7 @@ static TARGETS: &[&str] = &[ "loongarch64-unknown-none", "loongarch64-unknown-none-softfloat", "m68k-unknown-linux-gnu", + "m68k-unknown-none-elf", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf", "mips-unknown-linux-gnu", @@ -128,6 +129,8 @@ static TARGETS: &[&str] = &[ "mipsisa64r6el-unknown-linux-gnuabi64", "mipsel-unknown-linux-gnu", "mipsel-unknown-linux-musl", + "mips-mti-none-elf", + "mipsel-mti-none-elf", "nvptx64-nvidia-cuda", "powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", diff --git a/src/tools/cargo b/src/tools/cargo -Subproject fd784878cfa843e3e29a6654ecf564c62fae673 +Subproject 088d496082726091024f1689c124a0c3dccbd77 diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index bcb3193ad67..d6534fbaff9 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -17,6 +17,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 49622048050..dee7d028655 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -23,6 +23,8 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.ref }} + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Check Changelog @@ -63,6 +65,8 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install i686 dependencies if: matrix.host == 'i686-unknown-linux-gnu' @@ -74,7 +78,8 @@ jobs: - name: Install toolchain run: | rustup set default-host ${{ matrix.host }} - rustup show active-toolchain + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build @@ -121,9 +126,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install - name: Test metadata collection run: cargo collect-metadata @@ -136,9 +145,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build Integration Test @@ -188,9 +201,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Download - name: Download target dir @@ -205,7 +222,7 @@ jobs: # Run - name: Test ${{ matrix.integration }} run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + TOOLCHAIN=$(rustup show active-toolchain | head -n 1 | cut -f1 -d' ') rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 2e5b5bd41df..80523d91f4f 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -25,9 +25,14 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 32dc251c836..b42f3e7712f 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -22,19 +22,27 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Checkout uses: actions/checkout@v4 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Set tag name if: startswith(github.ref, 'refs/tags/') run: | - TAG=$(basename ${{ github.ref }}) + TAG=$(basename "${TAGNAME}") echo "TAG_NAME=$TAG" >> $GITHUB_ENV + env: + # Make sure that the reference gets expanded before injecting it + TAGNAME: ${{ github.ref }} - name: Set beta to true if: github.ref == 'refs/heads/beta' run: echo "BETA=true" >> $GITHUB_ENV diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 3cbda0b3824..64966f1d189 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^` # being the commit from `master` that is the base of the merge @@ -73,6 +75,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Cache lintcheck bin id: cache-lintcheck-bin @@ -103,6 +108,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Restore lintcheck bin uses: actions/cache/restore@v4 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 0d402fe7064..69d00dc027e 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -12,6 +12,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index b6033de9350..1770e8095a0 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,52 @@ document. ## Unreleased / Beta / In Rust Nightly -[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master) +[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master) + +## Rust 1.84 + +Current stable, released 2025-01-09 + +[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster) + +### New Lints + +* Added [`unnecessary_map_or`] to `style` + [#11796](https://github.com/rust-lang/rust-clippy/pull/11796) +* Added [`arbitrary_source_item_ordering`] to `restriction` + [#13376](https://github.com/rust-lang/rust-clippy/pull/13376) +* Added [`map_with_unused_argument_over_ranges`] to `restriction` + [#13034](https://github.com/rust-lang/rust-clippy/pull/13034) +* Added [`map_all_any_identity`] to `complexity` + [#13499](https://github.com/rust-lang/rust-clippy/pull/13499) +* Added [`needless_as_bytes`] to `complexity` + [#13437](https://github.com/rust-lang/rust-clippy/pull/13437) +* Added [`unnecessary_literal_bound`] to `pedantic` + [#13395](https://github.com/rust-lang/rust-clippy/pull/13395) +* Added [`manual_ignore_case_cmp`] to `perf` + [#13334](https://github.com/rust-lang/rust-clippy/pull/13334) +* Added [`regex_creation_in_loops`] to `perf` + [#13412](https://github.com/rust-lang/rust-clippy/pull/13412) + +### Moves and Deprecations + +* Moved [`manual_is_power_of_two`] to `pedantic` (From `complexity`, now allow-by-default) + [#13553](https://github.com/rust-lang/rust-clippy/pull/13553) +* Move [`module_name_repetitions`] to `restriction` (from `pedantic`) + [#13541](https://github.com/rust-lang/rust-clippy/pull/13541) + +### Enhancements + +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: + CoAP, MHz, GHz, and THz + [#13633](https://github.com/rust-lang/rust-clippy/pull/13633) + [#13460](https://github.com/rust-lang/rust-clippy/pull/13460) +* [`large_const_arrays`]: Changed the default of [`array-size-threshold`] to `16kb` (from `512kb`) + [#13485](https://github.com/rust-lang/rust-clippy/pull/13485) ## Rust 1.83 -Current stable, released 2024-11-28 +Released 2024-11-28 [View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster) @@ -5493,6 +5534,7 @@ Released 2018-09-13 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons +[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens @@ -6252,6 +6294,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 77cb0006ff8..efa59fecc0e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index df9b1bbe18f..2b2c096b049 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -83,7 +83,12 @@ As section headers, we use: ``` ### New Lints +* Added [`LINT`] to `GROUP` + ### Moves and Deprecations +* Moved [`LINT`] to `GROUP` (From `GROUP`, now LEVEL-by-default) +* Renamed `LINT` to [`LINT`] + ### Enhancements ### False Positive Fixes ### Suggestion Fixes/Improvements diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index 9c5d4b516db..b3126024b99 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -21,7 +21,7 @@ use clippy_utils::is_trait_method; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index ea1d7d11389..181e794e6e4 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -582,6 +582,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec<u32>, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index a7b0cc56ea1..f4789c9d030 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,5 +1,7 @@ avoid-breaking-exported-api = false +lint-inconsistent-struct-field-initializers = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 3f18a0bc7d2..c761e207c6b 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index bffa04f6f09..c616589c56e 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -532,6 +532,26 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec<u32>, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] literal_representation_threshold: u64 = 16384, diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index c6673859282..790dafa811f 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { } fields.push(ClippyConf { name, - lints, attrs: &conf[attrs_start..attrs_end], + lints, field: conf[field_start..].trim_end(), }); diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index c1f8e82f698..b575ac1bf4c 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 74153990266..380b094d017 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Makes a note of the current item for comparison with the next. cur_t = Some(CurItem { - order: module_level_order, item, + order: module_level_order, name: get_item_name(item), }); } @@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte ItemKind::Use(..) => Use, ItemKind::Static(..) => Static, ItemKind::Const(..) => Const, - ItemKind::Fn{ .. } => Fn, + ItemKind::Fn { .. } => Fn, ItemKind::Macro(..) => Macro, ItemKind::Mod(..) => Mod, ItemKind::ForeignMod { .. } => ForeignMod, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 32c28c09c36..8c91c65eaf7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -66,7 +66,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); - let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { + let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.next_back()) { first.span.with_hi(last.span.hi()) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index 67aa33ca06c..6057144bc6a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; +use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -32,12 +34,21 @@ pub(super) fn check<'tcx>( return false; } - let suggestion = if msrv.meets(msrvs::RAW_REF_OP) { + let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) { let operator_kind = match mutability { Mutability::Not => "const", Mutability::Mut => "mut", }; - format!("&raw {operator_kind} {snip}") + // Make sure that the span to be replaced doesn't include parentheses, that could break the + // suggestion. + let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { + expr.span + .with_lo(expr.span.lo() + BytePos(1)) + .with_hi(expr.span.hi() - BytePos(1)) + } else { + expr.span + }; + (format!("&raw {operator_kind} {snip}"), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -46,18 +57,10 @@ pub(super) fn check<'tcx>( Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", }; - format!("{std_or_core}::ptr::{macro_name}!({snip})") + (format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span) }; - span_lint_and_sugg( - cx, - BORROW_AS_PTR, - expr.span, - "borrow as raw pointer", - "try", - suggestion, - Applicability::MachineApplicable, - ); + span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app); return true; } false diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index c64c0e15144..d90cf124fe4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -836,11 +836,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { as_underscore::check(cx, expr, cast_to_hir); as_pointer_underscore::check(cx, cast_to, cast_to_hir); - let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv) - } else { - false - }; + let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR) + && borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv); if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 7451fb909ef..3ff10d850f8 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -372,6 +372,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, + crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 3b3a78cb115..c04a73c890f 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -3,8 +3,11 @@ use clippy_utils::numeric_literal; 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, StructTailExpr}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt}; +use rustc_hir::{ + Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, 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}; @@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { walk_expr(self, expr); } + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + match pat.kind { + PatKind::Expr(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) => { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; + }, + _ => {}, + } + walk_pat(self, pat) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 40377dd841e..3e2b7055de4 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,6 +1,6 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; @@ -70,7 +70,14 @@ pub fn check( && let typeck = cx.tcx.typeck_body(body_id) && let body = cx.tcx.hir().body(body_id) && let ret_ty = typeck.expr_ty(body.value) - && implements_trait(cx, ret_ty, future, &[]) + && implements_trait_with_env( + cx.tcx, + ty::TypingEnv::non_body_analysis(cx.tcx, owner_id.def_id), + ret_ty, + future, + Some(owner_id.def_id.to_def_id()), + &[], + ) && let ty::Coroutine(_, subs) = ret_ty.kind() && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index c835b81679b..7561a6cf2a7 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ parser.into_offset_iter(), &doc, Fragments { - fragments: &fragments, doc: &doc, + fragments: &fragments, }, )) } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 2327da0ccff..1f89cab9148 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -25,7 +25,7 @@ pub(super) fn check( // page. So associated items or impl blocks are not part of this list. ItemKind::Static(..) | ItemKind::Const(..) - | ItemKind::Fn{ .. } + | ItemKind::Fn { .. } | ItemKind::Macro(..) | ItemKind::Mod(..) | ItemKind::TyAlias(..) diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 70524e458c7..2bec4f2f99e 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: Vec::new(), - is_map_used: false, allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, + is_map_used: false, + edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), }; diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index fb9f2b1526e..8a5cf7f56d5 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,8 +55,8 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x), - PatKind::Path(_) | PatKind::Lit(_) => true, + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), + PatKind::Path(_) | PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 2cd48ef98e5..c0b4743fd71 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -182,7 +182,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when // T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic. // So we have a hack like this. - && generic_args.len() > 0 + && !generic_args.is_empty() { return; } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 4427edb752e..ef272c305d3 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && !predicates.is_empty() { Some(ImplTraitBound { + span: bound.span(), predicates, + trait_def_id, args: path.args.map_or([].as_slice(), |p| p.args), constraints: path.args.map_or([].as_slice(), |p| p.constraints), - trait_def_id, - span: bound.span(), }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 4fcd2abb769..39ff3c13bcc 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,19 +1,21 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; 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, StructTailExpr}; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does - /// Checks for struct constructors where all fields are shorthand and - /// the order of the field init shorthand in the constructor is inconsistent - /// with the order in the struct definition. + /// Checks for struct constructors where the order of the field + /// init in the constructor is inconsistent with the order in the + /// struct definition. /// /// ### Why is this bad? /// Since the order of fields in a constructor doesn't affect the @@ -59,16 +61,37 @@ declare_clippy_lint! { #[clippy::version = "1.52.0"] pub INCONSISTENT_STRUCT_CONSTRUCTOR, pedantic, - "the order of the field init shorthand is inconsistent with the order in the struct definition" + "the order of the field init is inconsistent with the order in the struct definition" } -declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +pub struct InconsistentStructConstructor { + lint_inconsistent_struct_field_initializers: bool, +} + +impl InconsistentStructConstructor { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + } + } +} + +impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let ExprKind::Struct(qpath, fields, base) = expr.kind - && fields.iter().all(|f| f.is_shorthand) - && !expr.span.from_expansion() + let ExprKind::Struct(_, fields, _) = expr.kind else { + return; + }; + let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); + let applicability = if all_fields_are_shorthand { + Applicability::MachineApplicable + } else if self.lint_inconsistent_struct_field_initializers { + Applicability::MaybeIncorrect + } else { + return; + }; + if !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() @@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { return; } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let StructTailExpr::Base(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!( - "{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let span = field_with_attrs_span(cx.tcx, fields.first().unwrap()) + .with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi()); if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { - span_lint_and_sugg( + span_lint_and_then( cx, INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, + span, "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + let msg = if all_fields_are_shorthand { + "try" + } else { + "if the field evaluation order doesn't matter, try" + }; + let sugg = suggestion(cx, fields, &def_order_map); + diag.span_suggestion(span, msg, sugg, applicability); + }, ); } } @@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map true } + +fn suggestion<'tcx>( + cx: &LateContext<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + def_order_map: &FxHashMap<Symbol, usize>, +) -> String { + let ws = fields + .windows(2) + .map(|w| { + let w0_span = field_with_attrs_span(cx.tcx, &w[0]); + let w1_span = field_with_attrs_span(cx.tcx, &w[1]); + let span = w0_span.between(w1_span); + snippet(cx, span, " ") + }) + .collect::<Vec<_>>(); + + let mut fields = fields.to_vec(); + fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]); + let field_snippets = fields + .iter() + .map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), "..")) + .collect::<Vec<_>>(); + + assert_eq!(field_snippets.len(), ws.len() + 1); + + let mut sugg = String::new(); + for i in 0..field_snippets.len() { + sugg += &field_snippets[i]; + if i < ws.len() { + sugg += &ws[i]; + } + } + sugg +} + +fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { + if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + field.span.with_lo(attr.span.lo()) + } else { + field.span + } +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 3ea758e176f..5418acc105e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::ty::implements_trait; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -8,8 +9,8 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, - ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, + ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -163,7 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) if is_empty_string(lit) => true, + PatKind::Expr(lit) => match lit.kind { + PatExprKind::Lit { lit, .. } => match lit.node { + LitKind::Str(lit, _) => lit.as_str().is_empty(), + _ => false, + }, + _ => false, + }, _ => false, } && !expr.span.from_expansion() @@ -620,18 +627,30 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); - cx.tcx - .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) - .any(|item| is_is_empty(cx, item)) - }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + match ty.kind() { + ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { + let is_empty = sym!(is_empty); + cx.tcx + .associated_items(principal.def_id()) + .filter_by_name_unhygienic(is_empty) + .any(|item| is_is_empty(cx, item)) + }), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Adt(id, _) => { + has_is_empty_impl(cx, id.did()) + || (cx.tcx.recursion_limit().value_within_limit(depth) + && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { + implements_trait(cx, ty, deref_id, &[]) + && cx + .get_associated_type(ty, deref_id, "Target") + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + })) + }, + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } } + + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d33013ba663..fad6f9d0880 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -649,7 +649,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index f88605fbea0..6be30f3c957 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( cx, label, inner_labels: label.into_iter().collect(), - is_finite: false, loop_depth: 0, + is_finite: false, }; loop_visitor.visit_block(loop_block); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 312bcb55a95..006addb987f 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span let mut vis = BodyVisitor { + macro_unsafe_blocks: Vec::new(), #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, - macro_unsafe_blocks: Vec::new(), - lint: self, - cx + cx, + lint: self }; vis.visit_body(body); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index bbb89bee835..aa59b047b16 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -2,14 +2,15 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::sugg::Sugg; -use rustc_ast::{BinOpKind, LitKind}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use clippy_config::Conf; @@ -138,9 +139,40 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() + && matches!( + lhs.kind, + ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }) | ExprKind::Unary(UnOp::Neg, Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), + .. + }) + ) { + format!("_{}", cx.typeck_results().expr_ty(rhs)) + } else { + String::new() + }; + let dividend_sugg_str = dividend_sugg.into_string(); + // If `dividend_sugg` has enclosing paren like `(-2048)` and we need to add type suffix in the + // suggestion message, we want to make a suggestion string before `div_ceil` like + // `(-2048_{type_suffix})`. + let suggestion_before_div_ceil = if has_enclosing_paren(÷nd_sugg_str) { + format!( + "{}{})", + ÷nd_sugg_str[..dividend_sugg_str.len() - 1].to_string(), + type_suffix + ) + } else { + format!("{dividend_sugg_str}{type_suffix}") + }; let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); - let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index dabfac3f613..506f4f6d9de 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.84.0"] pub MANUAL_IGNORE_CASE_CMP, perf, "manual case-insensitive ASCII comparison" diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30a..9860deba843 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,9 +7,9 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -114,8 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg, start); - let range = check_range(start, end); + let ty_sugg = get_ty_sugg(cx, arg); + let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } @@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } -fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { - if let ExprKind::Lit(lit) = bound_expr.kind - && let local_hid = path_to_local(arg)? - && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) +fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { + let local_hid = path_to_local(arg)?; + if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span { - let ty_str = match lit.node { - Char(_) => "char", - Byte(_) => "u8", - _ => return None, - }; - return Some((*ty_span, ty_str)); + let arg_type = cx.typeck_results().expr_ty(arg); + return Some((*ty_span, arg_type)); } None } @@ -145,7 +140,7 @@ fn check_is_ascii( span: Span, recv: &Expr<'_>, range: &CharRange, - ty_sugg: Option<(Span, &'_ str)>, + ty_sugg: Option<(Span, Ty<'_>)>, ) { let sugg = match range { CharRange::UpperChar => "is_ascii_uppercase", @@ -159,8 +154,8 @@ fn check_is_ascii( let mut app = Applicability::MachineApplicable; let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; - if let Some((ty_span, ty_str)) = ty_sugg { - suggestion.push((ty_span, format!("{recv}: {ty_str}"))); + if let Some((ty_span, ty)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty}"))); } span_lint_and_then( @@ -196,19 +191,33 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { } } -fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { +fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { if let ExprKind::Lit(start_lit) = &start.kind && let ExprKind::Lit(end_lit) = &end.kind { - match (&start_lit.node, &end_lit.node) { - (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, - (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, - (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, - (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, - (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, - _ => CharRange::Otherwise, - } + check_lit_range(start_lit, end_lit) } else { CharRange::Otherwise } } + +fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { + if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind + && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + { + check_lit_range(start_lit, end_lit) + } else { + CharRange::Otherwise + } +} + +fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index ffa3eacf354..2e5a92915d9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -38,14 +38,13 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> { - if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { - expr_as_i128(expr).map(|num| -num) - } else if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &PatExpr<'_>) -> Option<i128> { + if let PatExprKind::Lit { lit, negated } = expr.kind && let LitKind::Int(num, _) = lit.node { // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. - num.get().try_into().ok() + let n = i128::try_from(num.get()).ok()?; + Some(if negated { -n } else { n }) } else { None } @@ -58,7 +57,7 @@ struct Num { } impl Num { - fn new(expr: &Expr<'_>) -> Option<Self> { + fn new(expr: &PatExpr<'_>) -> Option<Self> { Some(Self { val: expr_as_i128(expr)?, span: expr.span, @@ -90,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns { let mut ranges_found = Vec::new(); for pat in pats { - if let PatKind::Lit(lit) = pat.kind + if let PatKind::Expr(lit) = pat.kind && let Some(num) = Num::new(lit) { numbers_found.insert(num.val); diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c..7e43d222a66 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet}; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -21,8 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { - if let ExprKind::Lit(lit) = arm_bool.kind { + let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + if let PatExprKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index b72a61a4384..28e05c273d5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -254,9 +254,11 @@ impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, - PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => { - Self::from_pat(cx, arena, pat) - }, + PatKind::Binding(.., Some(pat)) + | PatKind::Box(pat) + | PatKind::Deref(pat) + | PatKind::Ref(pat, _) + | PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat), PatKind::Never => Self::Never, PatKind::Struct(ref path, fields, _) => { let fields = @@ -309,9 +311,9 @@ impl<'a> NormalizedPat<'a> { ); Self::Tuple(None, pats) }, - PatKind::Lit(e) => match &e.kind { + PatKind::Expr(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), @@ -328,7 +330,7 @@ impl<'a> NormalizedPat<'a> { let start = match start { None => 0, Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => val.get(), LitKind::Char(val) => val.into(), LitKind::Byte(val) => val.into(), @@ -340,7 +342,7 @@ impl<'a> NormalizedPat<'a> { let (end, bounds) = match end { None => (u128::MAX, RangeEnd::Included), Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => (val.get(), bounds), LitKind::Char(val) => (val.into(), bounds), LitKind::Byte(val) => (val.into(), bounds), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 1267fc9d0a5..9f5b7c855a1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + if let PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) = arm.pat.kind && let LitKind::Str(symbol, _) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 1fd2ebcb54a..ac1eae07eff 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -583,11 +583,6 @@ declare_clippy_lint! { /// are the same on purpose, you can factor them /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). /// - /// ### Known problems - /// False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// /// ### Example /// ```rust,ignore /// match foo { diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 63bea586caf..0d5575efc22 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { - if let ItemKind::Fn{ .. } = item.kind { + if let ItemKind::Fn { .. } = item.kind { let output = cx .tcx .fn_sig(item.owner_id) @@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }); }, // Example: `5 => 5` - (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { - if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { + (PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { + if let PatExprKind::Lit { + lit: pat_spanned, + negated: false, + } = &pat_expr_expr.kind + { return pat_spanned.node == expr_spanned.node; } }, diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 856311899f2..4a5d3c516b8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = if let Some(lhs) = lhs { - ConstEvalCtxt::new(cx).eval(lhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; let rhs_const = if let Some(rhs) = rhs { - ConstEvalCtxt::new(cx).eval(rhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? @@ -57,8 +57,10 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(value) = pat.kind { - let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; + if let PatKind::Expr(value) = pat.kind { + let value = ConstEvalCtxt::new(cx) + .eval_pat_expr(value)? + .int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 264458a86ef..edac97344a0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,6 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, walk_span_to_context}; +use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -74,8 +74,8 @@ fn find_match_true<'tcx>( span: Span, message: &'static str, ) { - if let PatKind::Lit(lit) = pat.kind - && let ExprKind::Lit(lit) = lit.kind + if let PatKind::Expr(lit) = pat.kind + && let PatExprKind::Lit { lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; @@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_")); + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! @@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, - Applicability::MachineApplicable, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 3ca20479f8e..38f876fed80 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind + let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, @@ -343,6 +343,10 @@ impl<'a> PatState<'a> { matches!(self, Self::Wild) }, + PatKind::Guard(..) => { + matches!(self, Self::Wild) + }, + // Patterns for things which can only contain a single sub-pattern. PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { self.add_pat(cx, pat) @@ -380,7 +384,7 @@ impl<'a> PatState<'a> { PatKind::Wild | PatKind::Binding(_, _, _, None) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Path(_) | PatKind::Never diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs new file mode 100644 index 00000000000..208172980c9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Instance; +use rustc_span::{Span, sym}; + +use super::DOUBLE_ENDED_ITERATOR_LAST; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) { + let typeck = cx.typeck_results(); + + // if the "last" method is that of Iterator + if is_trait_method(cx, expr, sym::Iterator) + // if self implements DoubleEndedIterator + && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) + && let self_type = cx.typeck_results().expr_ty(self_expr) + && implements_trait(cx, self_type.peel_refs(), deiter_id, &[]) + // resolve the method definition + && let id = typeck.type_dependent_def_id(expr.hir_id).unwrap() + && let args = typeck.node_args(expr.hir_id) + && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) + // find the provided definition of Iterator::last + && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + // if the resolved method is the same as the provided definition + && fn_def.def_id() == last_def.def_id + { + span_lint_and_sugg( + cx, + DOUBLE_ENDED_ITERATOR_LAST, + call_span, + "called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator", + "try", + "next_back()".to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index 07a7a12b162..f7bb8c1d696 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ let mut applicability = Applicability::MachineApplicable; let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + let span = expr.span.with_lo(map_span.lo()); + // If the methods are separated with comments, we don't apply suggestion automatically. + if span_contains_comment(cx.tcx.sess.source_map(), span) { + applicability = Applicability::Unspecified; + } span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_span.lo()), + span, format!("called `map(..).flatten()` on `{caller_ty_name}`"), format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), format!("{method_to_use}({closure_snippet})"), diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 1f204de01da..05360144657 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; +use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -24,6 +25,16 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(sugg_span) = expr.span.trim_start(caller.span) { + // If the result of `.map(identity)` is used as a mutable reference, + // the caller must not be an immutable binding. + if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() + && let Some(hir_id) = path_to_local(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + { + return; + } + span_lint_and_sugg( cx, MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 810287fa541..51351f6b7cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -14,6 +14,7 @@ mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; mod collapsible_str_replace; +mod double_ended_iterator_last; mod drain_collect; mod err_expect; mod expect_fun_call; @@ -4284,6 +4285,32 @@ declare_clippy_lint! { "map of a trivial closure (not dependent on parameter) over a range" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced + /// with `DoubleEndedIterator::next_back`. + /// + /// ### Why is this bad? + /// + /// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if + /// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization, + /// `Iterator::last` cannot be optimized for `DoubleEndedIterator`. + /// + /// ### Example + /// ```no_run + /// let last_arg = "echo hello world".split(' ').last(); + /// ``` + /// Use instead: + /// ```no_run + /// let last_arg = "echo hello world".split(' ').next_back(); + /// ``` + #[clippy::version = "1.85.0"] + pub DOUBLE_ENDED_ITERATOR_LAST, + perf, + "using `Iterator::last` on a `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [ MAP_ALL_ANY_IDENTITY, MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, + DOUBLE_ENDED_ITERATOR_LAST, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4931,6 +4959,7 @@ impl Methods { false, ); } + double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 348f740e7dd..6993150fb57 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; @@ -77,7 +78,7 @@ fn handle_expr( if revert != is_all && let ExprKind::Path(path) = fn_path.kind && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"]) + && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index ea4984f83ad..2780c3f8af5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( captured_ids: HirIdSet, ) -> Option<Vec<IterFunction>> { let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: id, - seen_other: false, - cx, - current_mutably_captured_ids: HirIdSet::default(), illegal_mutable_capture_ids: captured_ids, + current_mutably_captured_ids: HirIdSet::default(), + cx, + uses: Vec::new(), hir_id_uses_map: FxHashMap::default(), current_statement_hir_id: None, + seen_other: false, + target: id, }; visitor.visit_block(block); if visitor.seen_other { diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index db2b9d4d92f..82e66a0500a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::STDIN; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; @@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"]) + && match_def_path(cx, recv_adt.did(), &STDIN) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c6d4ef5911e..8a99974394c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>( { Some(IterUsage { kind: IterUsageKind::NextTuple, - span: e.span, unwrap_kind: None, + span: e.span, }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index b5d8972d7aa..c27d1fb4903 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -124,30 +124,30 @@ pub(super) fn check( match lit.node { ast::LitKind::Bool(false) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + method_name: "any", has_args: true, has_generic_return: false, - method_name: "any", }); }, ast::LitKind::Bool(true) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + method_name: "all", has_args: true, has_generic_return: false, - method_name: "all", }); }, ast::LitKind::Int(Pu128(0), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { + method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "sum", }); }, ast::LitKind::Int(Pu128(1), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "product", }); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index 1199d289761..b7dbebe60a4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -96,11 +96,25 @@ pub(super) fn check<'a>( Sugg::hir(cx, non_binding_location, "") ))); - let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding) - .maybe_par() - .into_string(); + let mut app = Applicability::MachineApplicable; + let binop = make_binop( + op.node, + &Sugg::hir_with_applicability(cx, recv, "..", &mut app), + &inner_non_binding, + ); - (binop, "a standard comparison", Applicability::MaybeIncorrect) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + _ => binop, + } + } else { + binop + } + .into_string(); + + (sugg, "a standard comparison", app) } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 42107581ab4..964f1603f0e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { Node::Stmt(_) => return true, - Node::Block(..) => continue, + Node::Block(..) => {}, Node::Item(item) => { if let ItemKind::Fn { body: body_id, .. } = &item.kind && let output_ty = return_ty(cx, item.owner_id) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 121c4326d64..2572e186ce6 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method}; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -97,6 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, def_id: LocalDefId, ) { + let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); + if is_in_test(cx.tcx, hir_id) { + return; + } + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -136,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir().get_parent_item(hir_id).def_id; diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 1141728640d..29dcbaa9e62 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { // ignore main() if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index b18f18d89e5..05aa425de9e 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { return; } match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { let desc = "a function"; let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 3c47d0edfdc..5f7fde30f03 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -80,7 +81,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ mut self".to_string() } else { - format!("&{} mut self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} mut self") } }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), @@ -89,7 +91,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ self".to_string() } else { - format!("&{} self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} self") } }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index f69913ddbfd..7f91e555054 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; @@ -390,7 +390,7 @@ fn replace_types<'tcx>( projection_predicates: &[ProjectionPredicate<'tcx>], args: &mut [GenericArg<'tcx>], ) -> bool { - let mut replaced = BitSet::new_empty(args.len()); + let mut replaced = DenseBitSet::new_empty(args.len()); let mut deque = VecDeque::with_capacity(args.len()); deque.push_back((param_ty, new_ty)); diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index c48232f9905..05b31fc84b9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::ast; +use rustc_ast::{Block, Label, ast}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -11,6 +11,7 @@ declare_clippy_lint! { /// that contain a `continue` statement in either their main blocks or their /// `else`-blocks, when omitting the `else`-block possibly with some /// rearrangement of code can make the code easier to understand. + /// The lint also checks if the last statement in the loop is a `continue` /// /// ### Why is this bad? /// Having explicit `else` blocks for `if` statements @@ -75,6 +76,49 @@ declare_clippy_lint! { /// # break; /// } /// ``` + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// continue + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// continue + /// } + /// _ => { + /// eprintln!("other error"); + /// continue + /// } + /// } + /// } + /// ``` + /// Could be rewritten as + /// + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// } + /// _ => { + /// eprintln!("other error"); + /// } + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_CONTINUE, pedantic, @@ -144,7 +188,7 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), @@ -152,7 +196,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } } -fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { @@ -166,7 +210,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) } /// If the `continue` has a label, check it matches the label of the loop. -fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool { +fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> bool { match (loop_label, continue_label) { // `loop { continue; }` or `'a loop { continue; }` (_, None) => true, @@ -181,7 +225,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast:: /// the AST object representing the loop block of `expr`. fn with_loop_block<F>(expr: &ast::Expr, mut func: F) where - F: FnMut(&ast::Block, Option<&ast::Label>), + F: FnMut(&Block, Option<&Label>), { if let ast::ExprKind::While(_, loop_block, label) | ast::ExprKind::ForLoop { @@ -205,7 +249,7 @@ where /// - The `else` expression. fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr), + F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), { match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { @@ -231,14 +275,14 @@ struct LintData<'a> { /// The condition expression for the above `if`. if_cond: &'a ast::Expr, /// The `then` block of the `if` statement. - if_block: &'a ast::Block, + if_block: &'a Block, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. else_expr: &'a ast::Expr, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: usize, /// The statements of the loop block. - loop_block: &'a ast::Block, + loop_block: &'a Block, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -329,33 +373,74 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind - && let Some(last_stmt) = loop_block.stmts.last() +fn check_last_stmt_in_expr<F>(inner_expr: &ast::Expr, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + match &inner_expr.kind { + ast::ExprKind::Continue(continue_label) => { + func(continue_label.as_ref(), inner_expr.span); + }, + ast::ExprKind::If(_, then_block, else_block) => { + check_last_stmt_in_block(then_block, func); + if let Some(else_block) = else_block { + check_last_stmt_in_expr(else_block, func); + } + }, + ast::ExprKind::Match(_, arms, _) => { + for arm in arms { + if let Some(expr) = &arm.body { + check_last_stmt_in_expr(expr, func); + } + } + }, + ast::ExprKind::Block(b, _) => { + check_last_stmt_in_block(b, func); + }, + _ => {}, + } +} + +fn check_last_stmt_in_block<F>(b: &Block, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + if let Some(last_stmt) = b.stmts.last() && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(continue_label) = inner_expr.kind - && compare_labels(loop_label.as_ref(), continue_label.as_ref()) { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); + check_last_stmt_in_expr(inner_expr, func); } +} + +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { with_loop_block(expr, |loop_block, label| { - for (i, stmt) in loop_block.stmts.iter().enumerate() { + let p = |continue_label: Option<&Label>, span: Span| { + if compare_labels(label, continue_label) { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); + } + }; + + let stmts = &loop_block.stmts; + for (i, stmt) in stmts.iter().enumerate() { + let mut maybe_emitted_in_if = false; with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { let data = &LintData { - stmt_idx: i, if_expr, if_cond: cond, if_block: then_block, else_expr, + stmt_idx: i, loop_block, }; + + maybe_emitted_in_if = true; if needless_continue_in_else(else_expr, label) { emit_warning( cx, @@ -365,8 +450,14 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { ); } else if is_first_block_stmt_continue(then_block, label) { emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; } }); + + if i == stmts.len() - 1 && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } } }); } @@ -400,7 +491,7 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> { +fn span_of_first_expr_in_block(block: &Block) -> Option<Span> { block.stmts.first().map(|stmt| stmt.span) } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 92644456f63..ccd50758044 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -144,7 +144,7 @@ impl NoEffect { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn{ .. } = item.kind + && let ItemKind::Fn { .. } = item.kind && let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index ebd301d5156..8409d179b0f 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -189,6 +189,8 @@ impl<'tcx> NonCopyConst<'tcx> { } fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { + // No branch that we check (yet) should continue if val isn't a ValTree::Branch + let ty::ValTree::Branch(val) = val else { return false }; match *ty.kind() { // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. @@ -197,12 +199,13 @@ impl<'tcx> NonCopyConst<'tcx> { // contained value. ty::Adt(def, ..) if def.is_union() => false, ty::Array(ty, _) => val - .unwrap_branch() .iter() .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { - let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); + let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else { + return false; + }; + let variant_index = VariantIdx::from_u32(variant_index.to_u32()); fields .iter() .copied() @@ -215,12 +218,10 @@ impl<'tcx> NonCopyConst<'tcx> { .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) }, ty::Adt(def, args) => val - .unwrap_branch() .iter() .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Tuple(tys) => val - .unwrap_branch() .iter() .zip(tys) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 2fee1c72a91..56c4157d6fe 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, + exemptions: get_exemptions(interned_name).unwrap_or(&[]), }); } diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 65ef56fd211..0eca788c787 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -47,6 +47,7 @@ impl ArithmeticSideEffects { Self { allowed_binary, allowed_unary, + const_span: None, disallowed_int_methods: [ sym::saturating_div, sym::wrapping_div, @@ -55,7 +56,6 @@ impl ArithmeticSideEffects { ] .into_iter() .collect(), - const_span: None, expr_span: None, } } diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index d2529d4d9f8..668f09bbfd5 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { self.searcher = Some(PathbufPushSearcher { local_id: id, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), - err_span: local.span, init_val: *init_expr, arg: None, + name: name.name, + err_span: local.span, }); } } @@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { local_id: id, lhs_is_let: false, let_ty_span: None, - name: name.ident.name, - err_span: expr.span, init_val: *right, arg: None, + name: name.ident.name, + err_span: expr.span, }); } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 6a1d40334e7..a27f9b63114 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse { ExprKind::If(_, next_then, Some(next_els)) => { then = next_then; els = next_els; - continue; }, // else if without else ExprKind::If(..) => return, diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 4f46ca3c715..658d93e634c 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -17,9 +17,9 @@ declare_clippy_lint! { /// Checks for redundant redefinitions of local bindings. /// /// ### Why is this bad? - /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. + /// Redundant redefinitions of local bindings do not change behavior other than variable's lifetimes and are likely to be unintended. /// - /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. + /// These rebindings can be intentional to shorten the lifetimes of variables because they affect when the `Drop` implementation is called. Other than that, they do not affect your code's meaning but they _may_ affect `rustc`'s stack allocation. /// /// ### Example /// ```no_run @@ -41,7 +41,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, - correctness, + suspicious, "redundant redefinition of a local binding" } declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 6a5bf1b8045..9443dca154e 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -24,6 +24,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("(") /// ``` + /// + /// Use instead: + /// ```ignore + /// Regex::new("\(") + /// ``` #[clippy::version = "pre 1.29.0"] pub INVALID_REGEX, correctness, @@ -49,6 +54,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("^foobar") /// ``` + /// + /// Use instead: + /// ```ignore + /// str::starts_with("foobar") + /// ``` #[clippy::version = "pre 1.29.0"] pub TRIVIAL_REGEX, nursery, @@ -87,7 +97,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub REGEX_CREATION_IN_LOOPS, perf, "regular expression compilation performed in a loop" diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index c690696aefc..597bfddecbc 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } { let mut apa = AuxParamsAttr { - first_bind_ident: ident, first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, + first_bind_ident: ident, first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -395,8 +395,8 @@ impl Default for AuxParamsAttr { counter: 0, has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, - first_bind_ident: Ident::empty(), first_block_span: DUMMY_SP, + first_bind_ident: Ident::empty(), first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, last_bind_ident: Ident::empty(), diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 8c8f11569c8..d2d693eaa1f 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -203,14 +203,18 @@ impl SlowVectorInit { "len", ); - span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { - diag.span_suggestion( - vec_alloc.allocation_expr.span.source_callsite(), - "consider replacing this with", - format!("vec![0; {len_expr}]"), - Applicability::Unspecified, - ); - }); + let span_to_replace = slow_fill + .span + .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + span_lint_and_sugg( + cx, + SLOW_VECTOR_INITIALIZATION, + span_to_replace, + msg, + "consider replacing this with", + format!("vec![0; {len_expr}]"), + Applicability::Unspecified, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 0d85b1b858a..3834087f797 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + PatKind::Expr(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 78f0b7d121c..ff116800512 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> { fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { let mut v = IndexBindingVisitor { - found_used: false, - suggest_span: self.suggest_span, idx: idx_ident, + suggest_span: self.suggest_span, + found_used: false, }; for stmt in self.block.stmts { diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index a1d92c3ac71..82cc5155380 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::has_repr_attr; +use clippy_utils::{has_repr_attr, is_in_test}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -37,7 +37,10 @@ declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + if is_struct_with_trailing_zero_sized_array(cx, item) + && !has_repr_attr(cx, item.hir_id()) + && !is_in_test(cx.tcx, item.hir_id()) + { span_lint_and_help( cx, TRAILING_EMPTY_ARRAY, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 48d65eb15d9..26323af3122 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -30,17 +30,14 @@ pub(super) fn check<'tcx>( | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::OrderedFields(Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // ptr <-> ptr @@ -50,7 +47,6 @@ pub(super) fn check<'tcx>( { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // fat ptr <-> (*size, *size) diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 363aea8be72..43cce625c64 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl, CheckTyContext { is_in_trait_impl, - is_exported, in_body: matches!(fn_kind, FnKind::Closure), + is_exported, ..CheckTyContext::default() }); } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs index 8165a45bc5b..9f107fbeec0 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub UNNECESSARY_LITERAL_BOUND, pedantic, "detects &str that could be &'static str in function return types" diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 50a97579df7..8923484bb58 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; } @@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. - Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) + Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index c899b1868a6..d00bd7f2b3d 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let mut visitor = AsyncFnVisitor { cx, found_await: false, - async_depth: 0, await_in_async_block: None, + async_depth: 0, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck self.unused_async_fns.push(UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, def_id, + fn_span: span, + await_in_async_block: visitor.await_in_async_block, }); } } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 89bb429e265..eaa119b045f 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { - tcx: self.cx.tcx, is_mutated: false, local_id: unwrap_info.local_id, + tcx: self.cx.tcx, }; let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); @@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { } let mut v = UnwrappableVariablesVisitor { - cx, unwrappables: Vec::new(), + cx, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d2970c93f8e..4dcc8ac7fb0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -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, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + match lit.value.kind { + PatExprKind::Lit { lit, negated } => { + bind!(self, lit); + bind!(self, negated); + kind!("Lit{{ref {lit}, {negated} }}"); + self.lit(lit); + }, + PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), + PatExprKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + } + } + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { @@ -712,16 +733,22 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Ref({pat}, Mutability::{muta:?})"); self.pat(pat); }, - PatKind::Lit(lit_expr) => { + PatKind::Guard(pat, cond) => { + bind!(self, pat, cond); + kind!("Guard({pat}, {cond})"); + self.pat(pat); + self.expr(cond); + }, + PatKind::Expr(lit_expr) => { bind!(self, lit_expr); - kind!("Lit({lit_expr})"); - self.expr(lit_expr); + kind!("Expr({lit_expr})"); + self.pat_expr(lit_expr); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); + start.if_some(|e| self.pat_expr(e)); + end.if_some(|e| self.pat_expr(e)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index a5fad68eea1..08c178ed229 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -73,6 +73,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Slice, SimplifiedType::Str, SimplifiedType::Bool, + SimplifiedType::Char, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index ef1c46154d2..0730b561bc2 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (span, lint_opt) in &self.span_to_lint_map { if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc(),); + let help_msg = format!("you can use {} directly", suggest_slice.desc()); span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - diag.span_suggestion(*span, help_msg, snippet, *applicability); + // If the `vec!` macro contains comment, better not make the suggestion machine + // applicable as it would remove them. + let applicability = if *applicability != Applicability::Unspecified + && let source_map = cx.tcx.sess.source_map() + && span_contains_comment(source_map, *span) + { + Applicability::Unspecified + } else { + *applicability + }; + diag.span_suggestion(*span, help_msg, snippet, applicability); }); } } diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index cbc6885ae5d..d87d554eb07 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { local_id: id, init, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), + name: name.name, err_span: local.span, found: 0, last_push_expr: init_expr.hir_id, @@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { && name.ident.as_str() == "push" { self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, err_span: searcher.err_span.to(stmt.span), + found: searcher.found + 1, last_push_expr: expr.hir_id, ..searcher }); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index a42ddcdae35..31ae002e47d 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -248,8 +248,8 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests: conf.allow_print_in_tests, in_debug_impl: false, + allow_print_in_tests: conf.allow_print_in_tests, } } } diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index a702e0785a9..4df34891a2b 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus }; let mut vis = ExitPointFinder { - cx, state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + cx, }; if let Break(ExitCallFound) = vis.visit_block(block) { // Visitor found an unconditional `exit()` call, so don't lint. diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 945827c98c1..7fa070cd226 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 73fefbcd570..c267b804124 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2024-12-26 +nightly-2025-01-09 ``` <!-- end autogenerated nightly --> diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 623d9c76086..2eb09bac8d8 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, - (Lit(l), Lit(r)) => eq_expr(l, r), + (Expr(l), Expr(r)) => eq_expr(l, r), (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) }, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 43ddf06730d..d46beddf731 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,7 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use crate::macros::HirNode; use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant<'tcx>> { + match &pat_expr.kind { + PatExprKind::Lit { lit, negated } => { + let ty = self.typeck.node_type_opt(pat_expr.hir_id); + let val = lit_to_mir_constant(&lit.node, ty); + if *negated { + self.constant_negate(&val, ty?) + } else { + Some(val) + } + } + PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant<'tcx>> { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core + } else { + false + }; + self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; + // If source is already Constant we wouldn't want to override it with CoreConstant + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); + Some(result) + }) + } + /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) - }, + ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 4e12577b6df..60be7e4a4d3 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> { if let ExprKind::DropTemps(new_cond) = cond.kind { return Some(Self { cond: new_cond, - r#else, then, + r#else, }); } if let ExprKind::Let(..) = cond.kind { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ed52c481de1..a1c48d5c36c 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res}; 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, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, + Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, + TraitBoundModifiers, Ty, TyKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> { li.name == ri.name && self.eq_pat(lp, rp) } + fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { + match (&left.kind, &right.kind) { + ( + &PatExprKind::Lit { + lit: left, + negated: left_neg, + }, + &PatExprKind::Lit { + lit: right, + negated: right_neg, + }, + ) => left_neg == right_neg && left.node == right.node, + (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body), + (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right), + (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false, + } + } + /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { @@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { - both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b)) - && both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b)) + both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) + && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), @@ -788,8 +806,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - path_check: PathCheck::default(), s: FxHasher::default(), + path_check: PathCheck::default(), } } @@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) { + std::mem::discriminant(&lit.kind).hash(&mut self.s); + match &lit.kind { + PatExprKind::Lit { lit, negated } => { + lit.node.hash(&mut self.s); + negated.hash(&mut self.s); + }, + PatExprKind::ConstBlock(c) => self.hash_body(c.body), + PatExprKind::Path(qpath) => self.hash_qpath(qpath), + } + } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { @@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); @@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { - self.hash_expr(s); + self.hash_pat_expr(s); } if let Some(e) = e { - self.hash_expr(e); + self.hash_pat_expr(e); } std::mem::discriminant(&i).hash(&mut self.s); }, @@ -1104,6 +1134,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); std::mem::discriminant(&mu).hash(&mut self.s); }, + PatKind::Guard(pat, guard) => { + self.hash_pat(pat); + self.hash_expr(guard); + }, PatKind::Slice(l, m, r) => { for pat in l { self.hash_pat(pat); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 30e7471dd92..eecfc3fb13f 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let mut v = V { cx, - allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + allow_closure: true, captures: HirIdMap::default(), }; v.visit_expr(expr); @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true, + PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, } } diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 3924e384c37..ccbbccd0dbf 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, HirId}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, @@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { /// Checks if the block is part of a cycle pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); seen.insert(block); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 17e6558a41c..5eb9b3b8f22 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor; use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; @@ -21,19 +21,19 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>, + possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, } impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn new( cx: &'a LateContext<'tcx>, body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>, + possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, ) -> Self { Self { possible_borrower: TransitiveRelation::default(), - cx, body, + cx, possible_origin, } } @@ -56,7 +56,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { } } - let bs = BitSet::new_empty(self.body.local_decls.len()); + let bs = DenseBitSet::new_empty(self.body.local_decls.len()); PossibleBorrowerMap { map, maybe_live, @@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> { let mut mutable_variables: Vec<mir::Local> = mutable_borrowers .iter() .filter_map(|r| self.possible_origin.get(r)) - .flat_map(BitSet::iter) + .flat_map(DenseBitSet::iter) .collect(); if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { @@ -171,10 +171,10 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap<mir::Local, BitSet<mir::Local>>, + pub map: FxHashMap<mir::Local, DenseBitSet<mir::Local>>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>), + // Caches to avoid allocation of `DenseBitSet` on every query + pub bitset: (DenseBitSet<mir::Local>, DenseBitSet<mir::Local>), } impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { @@ -184,7 +184,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs index 47b93aad20c..3d253fd2bb1 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs @@ -1,7 +1,7 @@ use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { } } - pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, BitSet<mir::Local>> { + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, DenseBitSet<mir::Local>> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { if is_copy(cx, self.body.local_decls[row].ty) { diff --git a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs index 74d1f60af71..da44829a4c8 100644 --- a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs +++ b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir; #[derive(Default)] @@ -12,8 +12,8 @@ impl TransitiveRelation { self.relations.entry(a).or_default().push(b); } - pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> BitSet<mir::Local> { - let mut seen = BitSet::new_empty(domain_size); + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> DenseBitSet<mir::Local> { + let mut seen = DenseBitSet::new_empty(domain_size); let mut stack = vec![a]; while let Some(u) = stack.pop() { if let Some(edges) = self.relations.get(&u) { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 98bcedecccc..2169a5fdd63 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -130,7 +130,7 @@ impl Msrv { let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); if let Some(msrv_attr) = msrv_attrs.next() { - if let Some(duplicate) = msrv_attrs.last() { + if let Some(duplicate) = msrv_attrs.next_back() { sess.dcx() .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times") .with_span_note(msrv_attr.span(), "first definition found here") diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 8cb8cd59014..f15fffc09e8 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -33,6 +33,8 @@ pub const CHILD: [&str; 3] = ["std", "process", "Child"]; pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; +pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"]; +pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index e38036315c2..dcc1ec339ef 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -94,8 +94,8 @@ impl ClippyWarning { Some(Self { name, diag, - url, krate: krate.to_string(), + url, }) } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 1000d90f52a..b1f0a82b1f4 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-12-26" +channel = "nightly-2025-01-09" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index b8e0413e97b..e2e4d92df79 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -574,12 +574,12 @@ impl LintMetadata { id_location: None, group: "deprecated", level: "none", - version, docs: format!( "### What it does\n\n\ Nothing. This lint has been deprecated\n\n\ ### Deprecation reason\n\n{reason}.\n", ), + version, applicability: Applicability::Unspecified, } } diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index a8225d037e8..64eba5e0888 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec<String> { missing_files.push(path.to_str().unwrap().to_string()); } }, - _ => continue, + _ => {}, }; } } diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b99e8c0e76f..ff178924bd1 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,3 +1,4 @@ + thread '<unnamed>' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: Would you like some help with that? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml new file mode 100644 index 00000000000..f43c9d97e82 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -0,0 +1 @@ +lint-inconsistent-struct-field-initializers = true diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed new file mode 100644 index 00000000000..8092e40ff9f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { x, y, z: z }; + + Foo { + x, + z: z, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec<HirId>, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + macro_unsafe_blocks: Vec::new(), + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + a: 3, + b: 2, + #[cfg(all())] + c: 1, + d: 0, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + a: 3, + #[cfg(any())] + c: 1, + b: 2, + d: 0, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..cd1aff96652 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { y, x, z: z }; + + Foo { + z: z, + x, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec<HirId>, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(all())] + c: 1, + b: 2, + a: 3, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(any())] + c: 1, + b: 2, + a: 3, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr new file mode 100644 index 00000000000..d2533960b84 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr @@ -0,0 +1,77 @@ +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11 + | +LL | Foo { y, x, z: z }; + | ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9 + | +LL | / z: z, +LL | | x, + | |_________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ x, +LL ~ z: z, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13 + | +LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL | | expn_depth: if condition { 1 } else { 0 }, +LL | | macro_unsafe_blocks: Vec::new(), + | |___________________________________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ macro_unsafe_blocks: Vec::new(), +LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL ~ expn_depth: if condition { 1 } else { 0 }, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13 + | +LL | / d: 0, +LL | | #[cfg(all())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + b: 2, +LL + #[cfg(all())] +LL + c: 1, +LL ~ d: 0, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13 + | +LL | / d: 0, +LL | | #[cfg(any())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + #[cfg(any())] +LL + c: 1, +LL + b: 2, +LL ~ d: 0, + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 200129da25f..01e9f5c26a3 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -222,6 +224,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index a85dcddd331..8ffdf886202 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -30,8 +30,8 @@ if let StmtKind::Let(local) = stmt.kind } if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind - && let PatKind::Lit(lit_expr) = let_expr.pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_expr.pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.init.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 609d2491061..c94eb171f52 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -76,8 +76,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: // report your lint here } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) - && let PatKind::Lit(lit_expr) = let_pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 91b3b6f6877..acb3b140dfa 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -4,14 +4,14 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Lit(ref lit) = scrutinee.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 - && let PatKind::Lit(lit_expr) = arms[0].pat.kind - && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let PatKind::Expr(lit_expr) = arms[0].pat.kind + && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node - && let PatKind::Lit(lit_expr1) = arms[1].pat.kind - && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let PatKind::Expr(lit_expr1) = arms[1].pat.kind + && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index 0b332d5e7d0..b66bbccb3cf 100644 --- a/src/tools/clippy/tests/ui/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout @@ -23,8 +23,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" - && let PatKind::Lit(lit_expr) = fields[0].pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -36,8 +36,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 - && let PatKind::Lit(lit_expr) = fields[0].kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 289a5ef38b8..5365f3dd443 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = std::ptr::addr_of_mut!(val_mut); + + let mut x: [i32; 2] = [42, 43]; + let _raw = std::ptr::addr_of_mut!(x[1]).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&raw mut x[1]).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index b5328cb22dc..261894f1341 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = &mut val_mut as *mut i32; + + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr index ea618b06e2c..4595fa4f248 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -13,5 +13,17 @@ error: borrow as raw pointer LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` -error: aborting due to 2 previous errors +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:21:16 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])` + +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:26:17 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs index de220505c3e..a49d53fbbd3 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs @@ -47,6 +47,17 @@ impl<T> std::ops::Deref for StaticRef<T> { } } +// ICE regression test +mod issue12979 { + use std::cell::UnsafeCell; + + const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ()); + + fn main() { + let _x = &ATOMIC_TUPLE.0; + } +} + // use a tuple to make sure referencing a field behind a pointer isn't linted. const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) }; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr index 9a9028c8649..4cefcc28008 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:54:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); | ^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::borrow_interior_mutable_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:55:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); | ^^^^^^ @@ -20,7 +20,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:58:22 + --> tests/ui/borrow_interior_mutable_const/others.rs:69:22 | LL | let _once_ref = &ONCE_INIT; | ^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _once_ref = &ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:59:25 + --> tests/ui/borrow_interior_mutable_const/others.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:60:27 + --> tests/ui/borrow_interior_mutable_const/others.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:61:26 + --> tests/ui/borrow_interior_mutable_const/others.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; | ^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _once_mut = &mut ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:72:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; | ^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = &ATOMIC_TUPLE; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; | ^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _ = &ATOMIC_TUPLE.0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:74:19 + --> tests/ui/borrow_interior_mutable_const/others.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; | ^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:76:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); | ^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:81:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:92:13 | LL | let _ = ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:86:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:97:5 | LL | CELL.set(2); | ^^^^ @@ -108,7 +108,7 @@ LL | CELL.set(2); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:87:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:98:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs new file mode 100644 index 00000000000..f3ab9cebb7c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs @@ -0,0 +1,9 @@ +// ICE: #10972 +// asked to assemble constituent types of unexpected type: Binder(Foo, []) +#![feature(type_alias_impl_trait)] + +use std::fmt::Debug; +type Foo = impl Debug; +const FOO2: Foo = 22_u32; + +pub fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-13862.rs b/src/tools/clippy/tests/ui/crashes/ice-13862.rs new file mode 100644 index 00000000000..a5f010054b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13862.rs @@ -0,0 +1,19 @@ +#![crate_type = "lib"] +#![no_std] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +pub struct S<const N: u8>; + +impl<const N: u8> Future for S<N> { + type Output = (); + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { + todo!() + } +} + +pub fn f<const N: u8>() -> S<N> { + S +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed new file mode 100644 index 00000000000..06c48e33753 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').next_back() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = DeIterator.next_back(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + fn last(self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs new file mode 100644 index 00000000000..9c13b496d11 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').last() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = DeIterator.last(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + Some(()) + } + fn last(self) -> Option<Self::Item> { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option<Self::Item> { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr new file mode 100644 index 00000000000..b795c18a736 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr @@ -0,0 +1,17 @@ +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:5:18 + | +LL | s.split(' ').last() + | ^^^^^^ help: try: `next_back()` + | + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` + +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:22:24 + | +LL | let _ = DeIterator.last(); + | ^^^^^^ help: try: `next_back()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed index 4c324587c96..67bd3e4d279 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed @@ -60,7 +60,11 @@ mod with_base { let z = 1; // Should lint. - Foo { x, z, ..Default::default() }; + Foo { + x, + z, + ..Default::default() + }; // Should NOT lint because the order is consistent with the definition. Foo { diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr index 97bb7c789a7..c145eb2a239 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr @@ -1,21 +1,24 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:36:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:15 | LL | Foo { y, x, z }; - | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | ^^^^^^^ help: try: `x, y, z` | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:63:9 + --> tests/ui/inconsistent_struct_constructor.rs:64:13 | -LL | / Foo { -LL | | z, +LL | / z, LL | | x, -LL | | ..Default::default() -LL | | }; - | |_________^ help: try: `Foo { x, z, ..Default::default() }` + | |_____________^ + | +help: try + | +LL ~ x, +LL ~ z, + | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs index da95ba04b82..178e300ff5b 100644 --- a/src/tools/clippy/tests/ui/infinite_iter.rs +++ b/src/tools/clippy/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 7d8a584b022..d7d3d299349 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 58c374ab8cd..45e1349febd 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index 7a822a79494..e6680266f10 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:7:29 + --> tests/ui/iter_overeager_cloned.rs:12:29 | LL | let _: Option<String> = vec.iter().cloned().last(); | ^^^^^^^^^^---------------- @@ -10,7 +10,7 @@ LL | let _: Option<String> = vec.iter().cloned().last(); = help: to override `-D warnings` add `#[allow(clippy::iter_overeager_cloned)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:9:29 + --> tests/ui/iter_overeager_cloned.rs:14:29 | LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -18,7 +18,7 @@ LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); | help: try: `.next().cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:11:20 + --> tests/ui/iter_overeager_cloned.rs:16:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- @@ -29,7 +29,7 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:13:21 + --> tests/ui/iter_overeager_cloned.rs:18:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^----------------- @@ -37,7 +37,7 @@ LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | help: try: `.take(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:15:21 + --> tests/ui/iter_overeager_cloned.rs:20:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^----------------- @@ -45,7 +45,7 @@ LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | help: try: `.skip(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:17:13 + --> tests/ui/iter_overeager_cloned.rs:22:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -53,7 +53,7 @@ LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | help: try: `.nth(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:19:13 + --> tests/ui/iter_overeager_cloned.rs:24:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ @@ -69,7 +69,7 @@ LL ~ .flatten().cloned(); | error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:24:13 + --> tests/ui/iter_overeager_cloned.rs:29:13 | LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | ^^^^^^^^^^---------------------------------------- @@ -77,7 +77,7 @@ LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | help: try: `.filter(|&x| x.starts_with('2')).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:26:13 + --> tests/ui/iter_overeager_cloned.rs:31:13 | LL | let _ = vec.iter().cloned().find(|x| x == "2"); | ^^^^^^^^^^---------------------------- @@ -85,7 +85,7 @@ LL | let _ = vec.iter().cloned().find(|x| x == "2"); | help: try: `.find(|&x| x == "2").cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:30:17 + --> tests/ui/iter_overeager_cloned.rs:35:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -93,7 +93,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:31:17 + --> tests/ui/iter_overeager_cloned.rs:36:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -101,7 +101,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:37:17 + --> tests/ui/iter_overeager_cloned.rs:42:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -109,7 +109,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:38:17 + --> tests/ui/iter_overeager_cloned.rs:43:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -117,7 +117,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:45:9 + --> tests/ui/iter_overeager_cloned.rs:50:9 | LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | ^^^^------------------------------------------------------- @@ -125,7 +125,7 @@ LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:56:13 + --> tests/ui/iter_overeager_cloned.rs:61:13 | LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target) | ^^^^------------------------------------------------------------ @@ -133,7 +133,7 @@ LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target | help: try: `.filter(move |&S { a, b }| **a == 1 && b == &target).cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:60:13 + --> tests/ui/iter_overeager_cloned.rs:65:13 | LL | let _ = vec.iter().cloned().map(|x| x.len()); | ^^^^^^^^^^-------------------------- @@ -141,7 +141,7 @@ LL | let _ = vec.iter().cloned().map(|x| x.len()); | help: try: `.map(|x| x.len())` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:65:13 + --> tests/ui/iter_overeager_cloned.rs:70:13 | LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | ^^^^^^^^^^---------------------------------------------- @@ -149,7 +149,7 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | help: try: `.for_each(|x| assert!(!x.is_empty()))` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:67:13 + --> tests/ui/iter_overeager_cloned.rs:72:13 | LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- @@ -157,7 +157,7 @@ LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | help: try: `.all(|x| x.len() == 1)` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:69:13 + --> tests/ui/iter_overeager_cloned.rs:74:13 | LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index 27319d9c20e..c9c476ba421 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", (**d2s).is_empty()); + println!("{}", std::borrow::Cow::Borrowed("").is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (!has_is_empty.is_empty()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 03c05bc6ed7..610a5448d10 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", &**d2s == ""); + println!("{}", std::borrow::Cow::Borrowed("") == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr index 5c849a2aca6..8d6b57e4b6d 100644 --- a/src/tools/clippy/tests/ui/len_zero.stderr +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -58,107 +58,113 @@ error: comparison to empty slice LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` +error: comparison to empty slice + --> tests/ui/len_zero.rs:111:20 + | +LL | println!("{}", std::borrow::Cow::Borrowed("") == ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `std::borrow::Cow::Borrowed("").is_empty()` + error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:126:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:129:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:132:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:133:8 + --> tests/ui/len_zero.rs:135:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:136:8 + --> tests/ui/len_zero.rs:138:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:149:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:152:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:155:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:156:8 + --> tests/ui/len_zero.rs:158:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:159:8 + --> tests/ui/len_zero.rs:161:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:173:8 + --> tests/ui/len_zero.rs:175:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:185:6 + --> tests/ui/len_zero.rs:187:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:186:6 + --> tests/ui/len_zero.rs:188:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:190:8 + --> tests/ui/len_zero.rs:192:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:224:8 + --> tests/ui/len_zero.rs:226:8 | LL | if has_is_empty.len() == compare_to!(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:225:8 + --> tests/ui/len_zero.rs:227:8 | LL | if has_is_empty.len() == zero!() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:227:6 + --> tests/ui/len_zero.rs:229:6 | LL | (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index e7801f7376a..1fb1df5b442 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 2de74c7eaa8..4f6d38f0d14 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index dc652dff405..3d87fe8e040 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -31,5 +31,59 @@ error: manually reimplementing `div_ceil` LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` -error: aborting due to 5 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:34:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:37:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:40:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:43:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:45:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:46:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:47:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:49:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index a1d678c6689..f32b78aa14d 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _ = 2048_i32.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let x = -2; + let _ = (-2048_i32).div_ceil(x); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 58cb1dbe34d..54d89fcbd46 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _ = (2048 + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let x = -2; + let _ = (-2048 + x - 1) / x; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index 361ef9bd9f4..c5e8c1a687c 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -43,5 +43,71 @@ error: manually reimplementing `div_ceil` LL | let _ = (z_u + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` -error: aborting due to 7 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:29:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:32:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:35:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:38:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:40:13 + | +LL | let _ = (2048 + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:42:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:43:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:44:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:46:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:49:13 + | +LL | let _ = (-2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(-2048_i32).div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed index a72caa3a37e..179149f697d 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed @@ -82,3 +82,8 @@ fn generics() { take_while(|c: u8| c.is_ascii_uppercase()); take_while(|c: char| c.is_ascii_uppercase()); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs index bb6e2a317da..74f35ce94e8 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs @@ -82,3 +82,8 @@ fn generics() { take_while(|c| (b'A'..=b'Z').contains(&c)); take_while(|c: char| ('A'..='Z').contains(&c)); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr index a93ccace28a..92d93208006 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr @@ -173,5 +173,27 @@ error: manual check for common ascii range LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` -error: aborting due to 27 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:87:63 + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:88:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index 76916d46591..eafc8b6e81c 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -55,6 +55,18 @@ fn long_span() { .collect(); } +#[allow(clippy::useless_vec)] +fn no_suggestion_if_comments_present() { + let vec = vec![vec![1, 2, 3]]; + let _ = vec + .iter() + // a lovely comment explaining the code in very detail + .map(|x| x.iter()) + //~^ ERROR: called `map(..).flatten()` on `Iterator` + // the answer to life, the universe and everything could be here + .flatten(); +} + fn main() { long_span(); } diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr index a5837b97617..34bd174d7dd 100644 --- a/src/tools/clippy/tests/ui/map_flatten.stderr +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -102,5 +102,14 @@ LL + } LL + }) | -error: aborting due to 4 previous errors +error: called `map(..).flatten()` on `Iterator` + --> tests/ui/map_flatten.rs:64:10 + | +LL | .map(|x| x.iter()) + | __________^ +... | +LL | | .flatten(); + | |__________________^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| x.iter())` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 53ebfb40ba0..3257ddc6f72 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -61,3 +61,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied(); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index c646c056859..be3bb9a4f10 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -65,3 +65,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied().map(|(x, y)| (x, y)); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.map(|x| x).next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index 0a0dc9c8f07..aa3fc4ae0b5 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -73,5 +73,17 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 11 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:77:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:81:19 + | +LL | let _ = { it }.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index ca323dcf173..d2f9e34a5ce 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -47,7 +47,34 @@ fn get_y() -> u32 { Y } -// Don't lint entrypoint functions +#[cfg(test)] +mod with_test_fn { + #[derive(Clone, Copy)] + pub struct Foo { + pub n: u32, + } + + impl Foo { + #[must_use] + pub const fn new(n: u32) -> Foo { + Foo { n } + } + } + + #[test] + fn foo_is_copy() { + let foo = Foo::new(42); + let one = foo; + let two = foo; + _ = one; + _ = two; + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +// if we have `#[start]` and `#[test]` check `is_entrypoint_fn(cx, def_id.to_def_id())` is stopped +// working +#[allow(clippy::missing_const_for_fn)] #[start] fn init(num: isize, something: *const *const u8) -> isize { 1 diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index 9da60c687d4..530eb77d83d 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(&'r#struct self) {} + fn f2(&'r#struct mut self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index fc4ec5cb0b3..5a1ff96a11c 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(self: &'r#struct Self) {} + fn f2(self: &'r#struct mut Self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr index c653267f752..7ebbbaa122f 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr @@ -37,5 +37,17 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: aborting due to 6 previous errors +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:68:11 + | +LL | fn f1(self: &'r#struct Self) {} + | ^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct self` + +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:69:11 + | +LL | fn f2(self: &'r#struct mut Self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct mut self` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs index b6d8a8f61ae..334a2b32775 100644 --- a/src/tools/clippy/tests/ui/needless_continue.rs +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -87,6 +87,14 @@ fn simple_loop4() { } } +fn simple_loop5() { + loop { + println!("bleh"); + { continue } + //~^ ERROR: this `continue` expression is redundant + } +} + mod issue_2329 { fn condition() -> bool { unimplemented!() @@ -168,3 +176,60 @@ fn issue_13641() { } } } + +mod issue_4077 { + fn main() { + 'outer: loop { + 'inner: loop { + do_something(); + if some_expr() { + println!("bar-7"); + continue 'outer; + } else if !some_expr() { + println!("bar-8"); + continue 'inner; + } else { + println!("bar-9"); + continue 'inner; + } + } + } + + for _ in 0..10 { + match "foo".parse::<i32>() { + Ok(_) => do_something(), + Err(_) => { + println!("bar-10"); + continue; + }, + } + } + + loop { + if true { + } else { + // redundant `else` + continue; // redundant `continue` + } + } + + loop { + if some_expr() { + continue; + } else { + do_something(); + } + } + } + + // The contents of these functions are irrelevant, the purpose of this file is + // shown in main. + + fn do_something() { + std::process::exit(0); + } + + fn some_expr() -> bool { + true + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr index 0741ba69248..ec39d623419 100644 --- a/src/tools/clippy/tests/ui/needless_continue.stderr +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -63,7 +63,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:60:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -71,7 +71,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:68:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -91,8 +91,16 @@ LL | continue | = help: consider dropping the `continue` expression +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:93:11 + | +LL | { continue } + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + error: this `else` block is redundant - --> tests/ui/needless_continue.rs:136:24 + --> tests/ui/needless_continue.rs:144:24 | LL | } else { | ________________________^ @@ -117,7 +125,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> tests/ui/needless_continue.rs:143:17 + --> tests/ui/needless_continue.rs:151:17 | LL | / if condition() { LL | | @@ -137,12 +145,70 @@ LL | | } } error: this `continue` expression is redundant - --> tests/ui/needless_continue.rs:166:13 + --> tests/ui/needless_continue.rs:174:13 | LL | continue 'b; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: consider dropping the `continue` expression -error: aborting due to 9 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:190:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:193:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:203:21 + | +LL | continue; + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:210:20 + | +LL | } else { + | ____________________^ +LL | | // redundant `else` +LL | | continue; // redundant `continue` +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if true { + // merged code follows: + + } + +error: there is no need for an explicit `else` block for this `if` expression + --> tests/ui/needless_continue.rs:217:13 + | +LL | / if some_expr() { +LL | | continue; +LL | | } else { +LL | | do_something(); +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause + if some_expr() { + continue; + } + { + do_something(); + } + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index c9b76262d70..c7e0cd2610f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -137,3 +137,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = (*p).is_none(); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index a5f9caf659c..6d9a9f7f942 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -164,3 +164,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = matches!(*p, None); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 575f199be42..34d80f5ca78 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -209,5 +209,11 @@ error: redundant pattern matching, consider using `is_none()` LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` -error: aborting due to 30 previous errors +error: redundant pattern matching, consider using `is_none()` + --> tests/ui/redundant_pattern_matching_option.rs:172:17 + | +LL | let _ = matches!(*p, None); + | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 16f81019574..2ba87f41250 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -11,22 +11,22 @@ fn extend_vector() { // Extend with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.extend(repeat(0).take(len)); //~^ ERROR: slow zero-filling initialization //~| NOTE: `-D clippy::slow-vector-initialization` implied by `-D warnings` + vec1.extend(repeat(0).take(len)); // Extend with len expression let mut vec2 = Vec::with_capacity(len - 10); - vec2.extend(repeat(0).take(len - 10)); //~^ ERROR: slow zero-filling initialization + vec2.extend(repeat(0).take(len - 10)); // Extend with mismatching expression should not be warned let mut vec3 = Vec::with_capacity(24322); vec3.extend(repeat(0).take(2)); let mut vec4 = Vec::with_capacity(len); - vec4.extend(repeat(0).take(vec4.capacity())); //~^ ERROR: slow zero-filling initialization + vec4.extend(repeat(0).take(vec4.capacity())); } fn mixed_extend_resize_vector() { @@ -36,20 +36,20 @@ fn mixed_extend_resize_vector() { // Slow initialization let mut resized_vec = Vec::with_capacity(30); - resized_vec.resize(30, 0); //~^ ERROR: slow zero-filling initialization + resized_vec.resize(30, 0); let mut extend_vec = Vec::with_capacity(30); - extend_vec.extend(repeat(0).take(30)); //~^ ERROR: slow zero-filling initialization + extend_vec.extend(repeat(0).take(30)); } fn resize_vector() { // Resize with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize mismatch len let mut vec2 = Vec::with_capacity(200); @@ -57,39 +57,39 @@ fn resize_vector() { // Resize with len expression let mut vec3 = Vec::with_capacity(len - 10); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); let mut vec4 = Vec::with_capacity(len); - vec4.resize(vec4.capacity(), 0); //~^ ERROR: slow zero-filling initialization + vec4.resize(vec4.capacity(), 0); // Reinitialization should be warned vec1 = Vec::with_capacity(10); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); } fn from_empty_vec() { // Resize with constant expression let len = 300; let mut vec1 = Vec::new(); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize with len expression let mut vec3 = Vec::new(); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); // Reinitialization should be warned vec1 = Vec::new(); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); vec1 = vec![]; - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 353c677097b..7f4b9f7b67a 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -1,109 +1,122 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:14:5 + --> tests/ui/slow_vector_initialization.rs:13:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.extend(repeat(0).take(len)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +... | +LL | | vec1.extend(repeat(0).take(len)); + | |____________________________________^ help: consider replacing this with: `vec![0; len]` | = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:20:5 + --> tests/ui/slow_vector_initialization.rs:19:20 | -LL | let mut vec2 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec2.extend(repeat(0).take(len - 10)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec2.extend(repeat(0).take(len - 10)); + | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:28:5 + --> tests/ui/slow_vector_initialization.rs:27:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.extend(repeat(0).take(vec4.capacity())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.extend(repeat(0).take(vec4.capacity())); + | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:39:5 + --> tests/ui/slow_vector_initialization.rs:38:27 | -LL | let mut resized_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | resized_vec.resize(30, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut resized_vec = Vec::with_capacity(30); + | ___________________________^ +LL | | +LL | | resized_vec.resize(30, 0); + | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:43:5 + --> tests/ui/slow_vector_initialization.rs:42:26 | -LL | let mut extend_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | extend_vec.extend(repeat(0).take(30)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut extend_vec = Vec::with_capacity(30); + | __________________________^ +LL | | +LL | | extend_vec.extend(repeat(0).take(30)); + | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:51:5 + --> tests/ui/slow_vector_initialization.rs:50:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:60:5 + --> tests/ui/slow_vector_initialization.rs:59:20 | -LL | let mut vec3 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:64:5 + --> tests/ui/slow_vector_initialization.rs:63:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.resize(vec4.capacity(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.resize(vec4.capacity(), 0); + | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:69:5 + --> tests/ui/slow_vector_initialization.rs:68:12 | -LL | vec1 = Vec::with_capacity(10); - | ---------------------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::with_capacity(10); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:77:5 + --> tests/ui/slow_vector_initialization.rs:76:20 | -LL | let mut vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::new(); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:82:5 + --> tests/ui/slow_vector_initialization.rs:81:20 | -LL | let mut vec3 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::new(); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:87:5 + --> tests/ui/slow_vector_initialization.rs:86:12 | -LL | vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::new(); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:91:5 + --> tests/ui/slow_vector_initialization.rs:90:12 | -LL | vec1 = vec![]; - | ------ help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = vec![]; + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed index 4a66ca7ec91..252b6e5a98c 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.fixed +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs index 16a68e02d66..6c5655f3178 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.rs +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs index 309a5920dfd..ea3b8ff01af 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.rs +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -193,3 +193,17 @@ type C = ConstParamNoDefault<0>; type D = ConstParamNonZeroDefault<0>; fn main() {} + +#[cfg(test)] +mod tests { + pub struct Friend { + age: u8, + } + + #[test] + fn oldest_empty_is_none() { + struct Michael { + friends: [Friend; 0], + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 70b78ceca50..efea28e7045 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -3,15 +3,16 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; fn main() { // should trigger - let _ = (Some(5) == Some(5)); - let _ = (Some(5) != Some(5)); - let _ = (Some(5) == Some(5)); + let _ = Some(5) == Some(5); + let _ = Some(5) != Some(5); + let _ = Some(5) == Some(5); let _ = Some(5).is_some_and(|n| { let _ = n; 6 >= 5 @@ -21,10 +22,13 @@ fn main() { let _ = Some(5).is_some_and(|n| n == n); let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); let _ = Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5]); - let _ = (Ok::<i32, i32>(5) == Ok(5)); + let _ = Ok::<i32, i32>(5) == Ok(5); let _ = (Some(5) == Some(5)).then(|| 1); let _ = Some(5).is_none_or(|n| n == 5); let _ = Some(5).is_none_or(|n| 5 == n); + let _ = !(Some(5) == Some(5)); + let _ = (Some(5) == Some(5)) || false; + let _ = (Some(5) == Some(5)) as usize; macro_rules! x { () => { @@ -60,7 +64,7 @@ fn main() { #[derive(PartialEq)] struct S2; let r: Result<i32, S2> = Ok(4); - let _ = (r == Ok(8)); + let _ = r == Ok(8); // do not lint `Result::map_or(true, …)` let r: Result<i32, S2> = Ok(4); diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index 50757715977..05a0ca816ef 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -3,6 +3,7 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; @@ -28,6 +29,9 @@ fn main() { let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); let _ = Some(5).map_or(true, |n| n == 5); let _ = Some(5).map_or(true, |n| 5 == n); + let _ = !Some(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(false, |n| n == 5) || false; + let _ = Some(5).map_or(false, |n| n == 5) as usize; macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 890abb01228..2b78996d5f3 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -1,30 +1,30 @@ error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:12:13 + --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:13:13 + --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:14:13 + --> tests/ui/unnecessary_map_or.rs:15:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:18:13 + --> tests/ui/unnecessary_map_or.rs:19:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -42,88 +42,106 @@ LL ~ }); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:22:13 + --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:23:13 + --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:24:13 + --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:25:13 + --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:26:13 + --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:27:13 + --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::<i32, i32>(5) == Ok(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::<i32, i32>(5) == Ok(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:28:13 + --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:29:13 + --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:30:13 + --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:54:13 + --> tests/ui/unnecessary_map_or.rs:32:14 + | +LL | let _ = !Some(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:33:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) || false; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:34:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:59:13 + --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:60:13 + --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:61:13 + --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:66:13 + --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/useless_vec.rs b/src/tools/clippy/tests/ui/useless_vec.rs new file mode 100644 index 00000000000..880809f81d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.rs @@ -0,0 +1,15 @@ +//@no-rustfix: no suggestions + +#![warn(clippy::useless_vec)] + +// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13692>. +fn foo() { + // There should be no suggestion in this case. + let _some_variable = vec![ + //~^ useless_vec + 1, 2, // i'm here to stay + 3, 4, // but this one going away ;-; + ]; // that is life anyways +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/useless_vec.stderr b/src/tools/clippy/tests/ui/useless_vec.stderr new file mode 100644 index 00000000000..e47364fb06d --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.stderr @@ -0,0 +1,21 @@ +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:8:26 + | +LL | let _some_variable = vec![ + | __________________________^ +LL | | +LL | | 1, 2, // i'm here to stay +LL | | 3, 4, // but this one going away ;-; +LL | | ]; // that is life anyways + | |_____^ + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +help: you can use an array directly + | +LL ~ let _some_variable = [1, 2, // i'm here to stay +LL ~ 3, 4]; // that is life anyways + | + +error: aborting due to 1 previous error + diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 36f876bcdc6..4e2510ed9ab 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -39,18 +39,22 @@ macro_rules! string_enum { } impl FromStr for $name { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<Self, ()> { + fn from_str(s: &str) -> Result<Self, Self::Err> { match s { $($repr => Ok(Self::$variant),)* - _ => Err(()), + _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)), } } } } } +// Make the macro visible outside of this module, for tests. +#[cfg(test)] +pub(crate) use string_enum; + string_enum! { #[derive(Clone, Copy, PartialEq, Debug)] pub enum Mode { diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs index 92c80c27de0..4c942c51bae 100644 --- a/src/tools/compiletest/src/compute_diff.rs +++ b/src/tools/compiletest/src/compute_diff.rs @@ -31,7 +31,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma for result in diff::lines(expected, actual) { match result { - diff::Result::Left(str) => { + diff::Result::Left(s) => { if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { results.push(mismatch); mismatch = Mismatch::new(line_number - context_queue.len() as u32); @@ -41,11 +41,11 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma mismatch.lines.push(DiffLine::Context(line.to_owned())); } - mismatch.lines.push(DiffLine::Expected(str.to_owned())); + mismatch.lines.push(DiffLine::Expected(s.to_owned())); line_number += 1; lines_since_mismatch = 0; } - diff::Result::Right(str) => { + diff::Result::Right(s) => { if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { results.push(mismatch); mismatch = Mismatch::new(line_number - context_queue.len() as u32); @@ -55,18 +55,18 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma mismatch.lines.push(DiffLine::Context(line.to_owned())); } - mismatch.lines.push(DiffLine::Resulting(str.to_owned())); + mismatch.lines.push(DiffLine::Resulting(s.to_owned())); lines_since_mismatch = 0; } - diff::Result::Both(str, _) => { + diff::Result::Both(s, _) => { if context_queue.len() >= context_size { let _ = context_queue.pop_front(); } if lines_since_mismatch < context_size { - mismatch.lines.push(DiffLine::Context(str.to_owned())); + mismatch.lines.push(DiffLine::Context(s.to_owned())); } else if context_size > 0 { - context_queue.push_back(str); + context_queue.push_back(s); } line_number += 1; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 9e8443cd13c..9eb6887f7e6 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -32,7 +32,7 @@ use crate::{ColorConfig, json, stamp_file_path}; mod debugger; // Helper modules that implement test running logic for each test suite. -// tidy-alphabet-start +// tidy-alphabetical-start mod assembly; mod codegen; mod codegen_units; @@ -47,7 +47,7 @@ mod run_make; mod rustdoc; mod rustdoc_json; mod ui; -// tidy-alphabet-end +// tidy-alphabetical-end #[cfg(test)] mod tests; diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ee7aed2a39c..8a49d630535 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -353,8 +353,8 @@ impl TestCx<'_> { // to work correctly. // // See <https://github.com/rust-lang/rust/pull/122248> for more background. + let stage0_sysroot = build_root.join("stage0-sysroot"); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - let stage0_sysroot = build_root.join("stage0-sysroot"); rustc.arg("--sysroot").arg(&stage0_sysroot); } @@ -373,6 +373,15 @@ impl TestCx<'_> { // Compute dynamic library search paths for recipes. let recipe_dylib_search_paths = { let mut paths = base_dylib_search_paths.clone(); + + // For stage 0, we need to explicitly include the stage0-sysroot libstd dylib. + // See <https://github.com/rust-lang/rust/issues/135373>. + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { + paths.push( + stage0_sysroot.join("lib").join("rustlib").join(&self.config.host).join("lib"), + ); + } + paths.push(support_lib_path.parent().unwrap().to_path_buf()); paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); paths diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index fec746904de..43c6dc0a67e 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -66,3 +66,30 @@ fn is_test_test() { assert!(!is_test(&OsString::from("#a_dog_gif"))); assert!(!is_test(&OsString::from("~a_temp_file"))); } + +#[test] +fn string_enums() { + // These imports are needed for the macro-generated code + use std::fmt; + use std::str::FromStr; + + crate::common::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq)] + enum Animal { + Cat => "meow", + Dog => "woof", + } + } + + // General assertions, mostly to silence the dead code warnings + assert_eq!(Animal::VARIANTS.len(), 2); + assert_eq!(Animal::STR_VARIANTS.len(), 2); + + // Correct string conversions + assert_eq!(Animal::Cat, "meow".parse().unwrap()); + assert_eq!(Animal::Dog, "woof".parse().unwrap()); + + // Invalid conversions + let animal = "nya".parse::<Animal>(); + assert_eq!("unknown `Animal` variant: `nya`", animal.unwrap_err()); +} diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index d0413911014..cea234cc74c 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" build_helper = { path = "../../build_helper" } env_logger = "0.11" log = "0.4" -anyhow = { version = "1", features = ["backtrace"] } +anyhow = "1" humantime = "2" humansize = "2" sysinfo = { version = "0.31.2", default-features = false, features = ["disk"] } diff --git a/src/tools/replace-version-placeholder/src/main.rs b/src/tools/replace-version-placeholder/src/main.rs index 247e4e7e932..fb2838a4ea0 100644 --- a/src/tools/replace-version-placeholder/src/main.rs +++ b/src/tools/replace-version-placeholder/src/main.rs @@ -10,13 +10,13 @@ fn main() { let version_str = t!(std::fs::read_to_string(&version_path), version_path); let version_str = version_str.trim(); walk::walk_many( - &[&root_path.join("compiler"), &root_path.join("library")], - |path, _is_dir| { - walk::filter_dirs(path) - // We exempt these as they require the placeholder - // for their operation - || path.ends_with("compiler/rustc_attr/src/builtin.rs") - }, + &[ + &root_path.join("compiler"), + &root_path.join("library"), + &root_path.join("src/doc/rustc"), + &root_path.join("src/doc/rustdoc"), + ], + |path, _is_dir| walk::filter_dirs(path), &mut |entry, contents| { if !contents.contains(VERSION_PLACEHOLDER) { return; diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs index df5e5d8a2e8..3c0e9c82f0b 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -45,8 +45,7 @@ impl Rustdoc { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); - let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); - cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); + cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 46bae20054c..ec33009239c 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -6,6 +6,10 @@ on: pull_request: merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 @@ -103,7 +107,7 @@ jobs: - name: Run analysis-stats on the rust standard libraries if: matrix.os == 'ubuntu-latest' env: - RUSTC_BOOTSTRAP: 1 + RUSTC_BOOTSTRAP: 1 run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/ - name: clippy diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2323fdf5333..48b5f3aabfc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1375,7 +1375,6 @@ dependencies = [ "memmap2", "object 0.33.0", "paths", - "proc-macro-api", "proc-macro-test", "ra-ap-rustc_lexer", "span", @@ -1390,6 +1389,7 @@ version = "0.0.0" dependencies = [ "proc-macro-api", "proc-macro-srv", + "tt", ] [[package]] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 7f3abcccc47..9440123de70 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -79,6 +79,7 @@ span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" } +test-utils = { path = "./crates/test-utils", version = "0.0.0" } toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } @@ -93,7 +94,6 @@ ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } -test-utils = { path = "./crates/test-utils" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } @@ -203,6 +203,13 @@ new_ret_no_self = "allow" useless_asref = "allow" # Has false positives assigning_clones = "allow" +# Does not work with macros +vec_init_then_push = "allow" +# Our tests have a lot of these +literal_string_with_formatting_args = "allow" +# This lint has been empowered but now also triggers on cases where its invalid to do so +# due to it ignoring move analysis +unnecessary_map_or = "allow" ## Following lints should be tackled at some point too_many_arguments = "allow" diff --git a/src/tools/rust-analyzer/clippy.toml b/src/tools/rust-analyzer/clippy.toml index 8032c775ab0..1046cb3d56b 100644 --- a/src/tools/rust-analyzer/clippy.toml +++ b/src/tools/rust-analyzer/clippy.toml @@ -3,3 +3,7 @@ disallowed-types = [ { path = "std::collections::HashSet", reason = "use FxHashSet" }, { path = "std::collections::hash_map::RandomState", reason = "use BuildHasherDefault<FxHasher>"} ] + +disallowed-methods = [ + { path = "std::process::Command::new", reason = "use `toolchain::command` instead as it forces the choice of a working directory" }, +] diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index e86944eeb35..a0fc8c31eaf 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -490,21 +490,25 @@ impl CrateGraph { } } - pub fn sort_deps(&mut self) { - self.arena - .iter_mut() - .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); - } - /// Extends this crate graph by adding a complete second crate /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// + /// This will deduplicate the crates of the graph where possible. + /// Furthermore dependencies are sorted by crate id to make deduplication easier. + /// /// Returns a map mapping `other`'s IDs to the new IDs in `self`. pub fn extend( &mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths, ) -> FxHashMap<CrateId, CrateId> { + // Sorting here is a bit pointless because the input is likely already sorted. + // However, the overhead is small and it makes the `extend` method harder to misuse. + self.arena + .iter_mut() + .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); + + let m = self.len(); let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default(); for topo in topo { @@ -513,7 +517,8 @@ impl CrateGraph { crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); - let new_id = self.arena.alloc(crate_data.clone()); + let find = self.arena.iter().take(m).find_map(|(k, v)| (v == crate_data).then_some(k)); + let new_id = find.unwrap_or_else(|| self.arena.alloc(crate_data.clone())); id_map.insert(topo, new_id); } diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index 35c0c89c70c..84b91a527f0 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -45,8 +45,8 @@ impl From<CfgAtom> for CfgExpr { impl CfgExpr { #[cfg(feature = "tt")] - pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr { - next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) + pub fn parse<S: Copy>(tt: &tt::TopSubtree<S>) -> CfgExpr { + next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid) } /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. @@ -66,19 +66,19 @@ impl CfgExpr { } #[cfg(feature = "tt")] -fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> { +fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> { use intern::sym; + use tt::iter::TtElement; let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), + Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(CfgExpr::Invalid), }; - // Peek - let ret = match it.as_slice().first() { - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { - match it.as_slice().get(1) { + let ret = match it.peek() { + Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { + match it.remaining().flat_tokens().get(1) { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); @@ -87,9 +87,8 @@ fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<C _ => return Some(CfgExpr::Invalid), } } - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, mut sub_it)) => { it.next(); - let mut sub_it = subtree.token_trees.iter(); let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)); match name { s if s == sym::all => CfgExpr::All(subs.collect()), @@ -104,7 +103,7 @@ fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<C }; // Eat comma separator - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() { + if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = it.peek() { if punct.char == ',' { it.next(); } diff --git a/src/tools/rust-analyzer/crates/edition/Cargo.toml b/src/tools/rust-analyzer/crates/edition/Cargo.toml index 926b1e1cd41..0a9ae101eb1 100644 --- a/src/tools/rust-analyzer/crates/edition/Cargo.toml +++ b/src/tools/rust-analyzer/crates/edition/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "edition" version = "0.0.0" +description = "Rust edition support crate for rust-analyzer." rust-version.workspace = true edition.workspace = true license.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index c0547bc3c8d..37e2a99e60f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -1,6 +1,6 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. -use std::{borrow::Cow, hash::Hash, ops, slice}; +use std::{borrow::Cow, hash::Hash, ops}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -17,6 +17,7 @@ use syntax::{ AstPtr, }; use triomphe::Arc; +use tt::iter::{TtElement, TtIter}; use crate::{ db::DefDatabase, @@ -154,15 +155,15 @@ impl Attrs { pub fn has_doc_hidden(&self) -> bool { self.by_key(&sym::doc).tt_values().any(|tt| { - tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) + tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && + matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) }) } pub fn has_doc_notable_trait(&self) -> bool { self.by_key(&sym::doc).tt_values().any(|tt| { - tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) + tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && + matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) }) } @@ -245,8 +246,8 @@ impl From<DocAtom> for DocExpr { } impl DocExpr { - fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr { - next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid) + fn parse<S: Copy>(tt: &tt::TopSubtree<S>) -> DocExpr { + next_doc_expr(tt.iter()).unwrap_or(DocExpr::Invalid) } pub fn aliases(&self) -> &[Symbol] { @@ -260,32 +261,29 @@ impl DocExpr { } } -fn next_doc_expr<S>(it: &mut slice::Iter<'_, tt::TokenTree<S>>) -> Option<DocExpr> { +fn next_doc_expr<S: Copy>(mut it: TtIter<'_, S>) -> Option<DocExpr> { let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), + Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(DocExpr::Invalid), }; // Peek - let ret = match it.as_slice().first() { - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { - match it.as_slice().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + let ret = match it.peek() { + Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { + it.next(); + match it.next() { + Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. - }))) => { - it.next(); - it.next(); - DocAtom::KeyValue { key: name, value: text.clone() }.into() - } + }))) => DocAtom::KeyValue { key: name, value: text.clone() }.into(), _ => return Some(DocExpr::Invalid), } } - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, subtree_iter)) => { it.next(); - let subs = parse_comma_sep(subtree); + let subs = parse_comma_sep(subtree_iter); match &name { s if *s == sym::alias => DocExpr::Alias(subs), _ => DocExpr::Invalid, @@ -293,29 +291,17 @@ fn next_doc_expr<S>(it: &mut slice::Iter<'_, tt::TokenTree<S>>) -> Option<DocExp } _ => DocAtom::Flag(name).into(), }; - - // Eat comma separator - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() { - if punct.char == ',' { - it.next(); - } - } Some(ret) } -fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<Symbol> { - subtree - .token_trees - .iter() - .filter_map(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, - symbol, - .. - })) => Some(symbol.clone()), - _ => None, - }) - .collect() +fn parse_comma_sep<S>(iter: TtIter<'_, S>) -> Vec<Symbol> { + iter.filter_map(|tt| match tt { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, symbol, .. + })) => Some(symbol.clone()), + _ => None, + }) + .collect() } impl AttrsWithOwner { @@ -563,7 +549,7 @@ pub struct AttrQuery<'attr> { } impl<'attr> AttrQuery<'attr> { - pub fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::Subtree> { + pub fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::TopSubtree> { self.attrs().filter_map(|attr| attr.token_tree_value()) } @@ -585,7 +571,7 @@ impl<'attr> AttrQuery<'attr> { pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone { let key = self.key; - self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key)) + self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == *key)) } /// Find string value for a specific key inside token tree @@ -596,12 +582,12 @@ impl<'attr> AttrQuery<'attr> { /// ``` pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&'attr str> { self.tt_values().find_map(|tt| { - let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) + let name = tt.iter() + .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), + Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), _ => None } }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index eed9f9468fd..10b84d041bd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -2216,11 +2216,11 @@ impl ExprCollector<'_> { }; // This needs to match `Flag` in library/core/src/fmt/rt.rs. let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) - | ((sign == Some(FormatSign::Minus)) as u32) << 1 - | (alternate as u32) << 2 - | (zero_pad as u32) << 3 - | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4 - | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5; + | (((sign == Some(FormatSign::Minus)) as u32) << 1) + | ((alternate as u32) << 2) + | ((zero_pad as u32) << 3) + | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4) + | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5); let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( flags as u128, Some(BuiltinUint::U32), @@ -2468,7 +2468,7 @@ impl ExprCollector<'_> { fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool { (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() - .map_or(false, |it| it.kind() == syntax::T![,]) + .is_some_and(|it| it.kind() == syntax::T![,]) } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 13ba4db6064..7e15a9f2d61 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -52,6 +52,7 @@ fn your_stack_belongs_to_me() { cov_mark::check!(your_stack_belongs_to_me); lower( r#" +#![recursion_limit = "32"] macro_rules! n_nuple { ($e:tt) => (); ($($rest:tt)*) => {{ @@ -68,6 +69,7 @@ fn your_stack_belongs_to_me2() { cov_mark::check!(overflow_but_not_me); lower( r#" +#![recursion_limit = "32"] macro_rules! foo { () => {{ foo!(); foo!(); }} } @@ -78,8 +80,6 @@ fn main() { foo!(); } #[test] fn recursion_limit() { - cov_mark::check!(your_stack_belongs_to_me); - lower( r#" #![recursion_limit = "2"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 15dd6aba311..d85bc9a4320 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -11,6 +11,7 @@ use la_arena::{Idx, RawIdx}; use smallvec::SmallVec; use syntax::{ast, Parse}; use triomphe::Arc; +use tt::iter::TtElement; use crate::{ db::DefDatabase, @@ -156,20 +157,21 @@ impl FunctionData { } } -fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> { +fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> { let mut indices = Vec::new(); - for args in tt.token_trees.chunks(2) { - match &args[0] { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { + let mut iter = tt.iter(); + while let (Some(first), second) = (iter.next(), iter.next()) { + match first { + TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { Ok(index) => indices.push(index), Err(_) => break, }, _ => break, } - if let Some(comma) = args.get(1) { + if let Some(comma) = second { match comma { - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {} + TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {} _ => break, } } @@ -227,20 +229,24 @@ impl TypeAliasData { } } +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] + pub struct TraitFlags: u8 { + const IS_AUTO = 1 << 0; + const IS_UNSAFE = 1 << 1; + const IS_FUNDAMENTAL = 1 << 2; + const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3; + const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4; + const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5; + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitData { pub name: Name, pub items: Vec<(Name, AssocItemId)>, - pub is_auto: bool, - pub is_unsafe: bool, - pub rustc_has_incoherent_inherent_impls: bool, - pub skip_array_during_method_dispatch: bool, - pub skip_boxed_slice_during_method_dispatch: bool, - pub fundamental: bool, + pub flags: TraitFlags, pub visibility: RawVisibility, - /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore - /// method calls to this trait's methods when the receiver is an array and the crate edition is - /// 2015 or 2018. // box it as the vec is usually empty anyways pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, } @@ -259,42 +265,50 @@ impl TraitData { let item_tree = tree_id.item_tree(db); let tr_def = &item_tree[tree_id.value]; let name = tr_def.name.clone(); - let is_auto = tr_def.is_auto; - let is_unsafe = tr_def.is_unsafe; let visibility = item_tree[tr_def.visibility].clone(); let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()); + + let mut flags = TraitFlags::empty(); + + if tr_def.is_auto { + flags |= TraitFlags::IS_AUTO; + } + if tr_def.is_unsafe { + flags |= TraitFlags::IS_UNSAFE; + } + if attrs.by_key(&sym::fundamental).exists() { + flags |= TraitFlags::IS_FUNDAMENTAL; + } + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { + flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; + } + let mut skip_array_during_method_dispatch = attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists(); let mut skip_boxed_slice_during_method_dispatch = false; for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() { - for tt in tt.token_trees.iter() { - if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { + for tt in tt.iter() { + if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt { skip_array_during_method_dispatch |= ident.sym == sym::array; skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; } } } - let rustc_has_incoherent_inherent_impls = - attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); - let fundamental = attrs.by_key(&sym::fundamental).exists(); + + if skip_array_during_method_dispatch { + flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH; + } + if skip_boxed_slice_during_method_dispatch { + flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH; + } + let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); let (items, macro_calls, diagnostics) = collector.finish(); ( - Arc::new(TraitData { - name, - macro_calls, - items, - is_auto, - is_unsafe, - visibility, - skip_array_during_method_dispatch, - skip_boxed_slice_during_method_dispatch, - rustc_has_incoherent_inherent_impls, - fundamental, - }), + Arc::new(TraitData { name, macro_calls, items, visibility, flags }), DefDiagnostics::new(diagnostics), ) } @@ -421,7 +435,7 @@ impl Macro2Data { .by_key(&sym::rustc_builtin_macro) .tt_values() .next() - .and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees)) + .and_then(parse_macro_name_and_helper_attrs) .map(|(_, helpers)| helpers); Arc::new(Macro2Data { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 068ebb3b7e9..8fc19854033 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -10,6 +10,7 @@ use intern::sym; use la_arena::Arena; use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use triomphe::Arc; +use tt::iter::TtElement; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, @@ -20,7 +21,7 @@ use crate::{ }, lang_item::LangItem, nameres::diagnostics::{DefDiagnostic, DefDiagnostics}, - tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, + tt::{Delimiter, DelimiterKind, Leaf, TopSubtree}, type_ref::{TypeRefId, TypesMap}, visibility::RawVisibility, EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId, @@ -95,8 +96,8 @@ fn repr_from_value( item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt) } -fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> { - match tt.delimiter { +fn parse_repr_tt(tt: &TopSubtree) -> Option<ReprOptions> { + match tt.top_subtree().delimiter { Delimiter { kind: DelimiterKind::Parenthesis, .. } => {} _ => return None, } @@ -106,14 +107,14 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> { let mut max_align: Option<Align> = None; let mut min_pack: Option<Align> = None; - let mut tts = tt.token_trees.iter().peekable(); + let mut tts = tt.iter(); while let Some(tt) = tts.next() { - if let TokenTree::Leaf(Leaf::Ident(ident)) = tt { + if let TtElement::Leaf(Leaf::Ident(ident)) = tt { flags.insert(match &ident.sym { s if *s == sym::packed => { - let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() { + let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { tts.next(); - if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { + if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() { lit.symbol.as_str().parse().unwrap_or_default() } else { 0 @@ -127,9 +128,9 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> { ReprFlags::empty() } s if *s == sym::align => { - if let Some(TokenTree::Subtree(tt)) = tts.peek() { + if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { tts.next(); - if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { + if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() { if let Ok(align) = lit.symbol.as_str().parse() { let align = Align::from_bytes(align).ok(); max_align = max_align.max(align); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index d7e83ce33e8..bf6cc1dcade 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -21,6 +21,7 @@ use crate::{ item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps}, lang_item::{self, LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostics, DefMap}, + tt, type_ref::TypesSourceMap, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, @@ -294,14 +295,14 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { // This is a `cfg_attr`; check if it could possibly expand to `no_std`. // Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]` let tt = match attr.token_tree_value() { - Some(tt) => &tt.token_trees, + Some(tt) => tt.token_trees(), None => continue, }; let segments = - tt.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); + tt.split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); for output in segments.skip(1) { - match output { + match output.flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => { return true } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 22005695af6..0f73595347b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -90,7 +90,7 @@ pub mod keys { map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(key) } fn is_empty(map: &DynMap) -> bool { - map.map.get::<FxHashMap<AstPtr<AST>, ID>>().map_or(true, |it| it.is_empty()) + map.map.get::<FxHashMap<AstPtr<AST>, ID>>().is_none_or(|it| it.is_empty()) } } } @@ -141,7 +141,7 @@ impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) { map.map.get::<FxHashMap<K, V>>()?.get(key) } fn is_empty(map: &DynMap) -> bool { - map.map.get::<FxHashMap<K, V>>().map_or(true, |it| it.is_empty()) + map.map.get::<FxHashMap<K, V>>().is_none_or(|it| it.is_empty()) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 163211fea52..c31d3221328 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -2,7 +2,7 @@ use expect_test::expect; -use crate::macro_expansion_tests::check; +use crate::macro_expansion_tests::{check, check_errors}; #[test] fn test_copy_expand_simple() { @@ -16,7 +16,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl < > $crate::marker::Copy for Foo< > where {}"#]], +impl <> $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -40,7 +40,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > $crate::marker::Copy for Foo< > where {}"#]], +impl <> $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -225,14 +225,14 @@ enum Bar { Bar, } -impl < > $crate::default::Default for Foo< > where { +impl <> $crate::default::Default for Foo< > where { fn default() -> Self { Foo { field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), } } } -impl < > $crate::default::Default for Bar< > where { +impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } @@ -260,7 +260,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialEq for Command< > where { +impl <> $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -273,7 +273,7 @@ impl < > $crate::cmp::PartialEq for Command< > where { } } } -impl < > $crate::cmp::Eq for Command< > where {}"#]], +impl <> $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -298,7 +298,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialEq for Command< > where { +impl <> $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -311,7 +311,7 @@ impl < > $crate::cmp::PartialEq for Command< > where { } } } -impl < > $crate::cmp::Eq for Command< > where {}"#]], +impl <> $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -335,7 +335,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialOrd for Command< > where { +impl <> $crate::cmp::PartialOrd for Command< > where { fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> { match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { @@ -370,7 +370,7 @@ impl < > $crate::cmp::PartialOrd for Command< > where { } } } -impl < > $crate::cmp::Ord for Command< > where { +impl <> $crate::cmp::Ord for Command< > where { fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { $crate::cmp::Ordering::Equal=> { @@ -432,7 +432,7 @@ struct Foo { z: (i32, u64), } -impl < > $crate::hash::Hash for Foo< > where { +impl <> $crate::hash::Hash for Foo< > where { fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) { match self { Foo { @@ -470,7 +470,7 @@ enum Command { Jump, } -impl < > $crate::hash::Hash for Command< > where { +impl <> $crate::hash::Hash for Command< > where { fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) { $crate::mem::discriminant(self ).hash(ra_expand_state); match self { @@ -516,7 +516,7 @@ enum Command { Jump, } -impl < > $crate::fmt::Debug for Command< > where { +impl <> $crate::fmt::Debug for Command< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { Command::Move { @@ -578,7 +578,7 @@ enum HideAndShowEnum { } } -impl < > $crate::fmt::Debug for HideAndShow< > where { +impl <> $crate::fmt::Debug for HideAndShow< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { HideAndShow { @@ -588,7 +588,7 @@ impl < > $crate::fmt::Debug for HideAndShow< > where { } } } -impl < > $crate::fmt::Debug for HideAndShowEnum< > where { +impl <> $crate::fmt::Debug for HideAndShowEnum< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { HideAndShowEnum::AlwaysShow { @@ -640,17 +640,109 @@ enum Bar { Bar, } -impl < > $crate::default::Default for Foo< > where { +impl <> $crate::default::Default for Foo< > where { fn default() -> Self { Foo { field1: $crate::default::Default::default(), field4: $crate::default::Default::default(), } } } -impl < > $crate::default::Default for Bar< > where { +impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } }"##]], ); } + +#[test] +fn coerce_pointee_expansion() { + check( + r#" +//- minicore: coerce_pointee + +use core::marker::CoercePointee; + +pub trait Trait<T: ?Sized> {} + +#[derive(CoercePointee)] +#[repr(transparent)] +pub struct Foo<'a, T: ?Sized + Trait<U>, #[pointee] U: ?Sized, const N: u32>(T) +where + U: Trait<U> + ToString;"#, + expect![[r#" + +use core::marker::CoercePointee; + +pub trait Trait<T: ?Sized> {} + +#[derive(CoercePointee)] +#[repr(transparent)] +pub struct Foo<'a, T: ?Sized + Trait<U>, #[pointee] U: ?Sized, const N: u32>(T) +where + U: Trait<U> + ToString; +impl <T, U, const N: u32, __S> $crate::ops::DispatchFromDyn<Foo<'a, T, __S, N>> for Foo<T, U, N, > where U: Trait<U> +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait<U>, U:?Sized, {} +impl <T, U, const N: u32, __S> $crate::ops::CoerceUnsized<Foo<'a, T, __S, N>> for Foo<T, U, N, > where U: Trait<U> +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait<U>, U:?Sized, {}"#]], + ); +} + +#[test] +fn coerce_pointee_errors() { + check_errors( + r#" +//- minicore: coerce_pointee + +use core::marker::CoercePointee; + +#[derive(CoercePointee)] +enum Enum {} + +#[derive(CoercePointee)] +struct Struct1; + +#[derive(CoercePointee)] +struct Struct2(); + +#[derive(CoercePointee)] +struct Struct3 {} + +#[derive(CoercePointee)] +struct Struct4<T: ?Sized>(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct5(i32); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct6<#[pointee] T: ?Sized, #[pointee] U: ?Sized>(T, U); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct7<T: ?Sized, U: ?Sized>(T, U); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct8<#[pointee] T, U: ?Sized>(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct9<T>(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct9<#[pointee] T, U>(T) where T: ?Sized; +"#, + expect![[r#" + 35..72: `CoercePointee` can only be derived on `struct`s + 74..114: `CoercePointee` can only be derived on `struct`s with at least one field + 116..158: `CoercePointee` can only be derived on `struct`s with at least one field + 160..202: `CoercePointee` can only be derived on `struct`s with at least one field + 204..258: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + 260..326: `CoercePointee` can only be derived on `struct`s that are generic over at least one type + 328..439: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + 441..530: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + 532..621: `derive(CoercePointee)` requires `T` to be marked `?Sized` + 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 0475e40c5b2..5b9ffdf37be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,14 +16,16 @@ mod proc_macros; use std::{iter, ops::Range, sync}; +use base_db::SourceDatabase; use expect_test::Expect; use hir_expand::{ db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, span_map::SpanMapRef, - InFile, MacroFileId, MacroFileIdExt, + InFile, MacroCallKind, MacroFileId, MacroFileIdExt, }; use intern::Symbol; +use itertools::Itertools; use span::{Edition, Span}; use stdx::{format_to, format_to_acc}; use syntax::{ @@ -40,11 +42,41 @@ use crate::{ resolver::HasResolver, src::HasSource, test_db::TestDB, - tt::Subtree, + tt::TopSubtree, AdtId, AsMacroCall, Lookup, ModuleDefId, }; #[track_caller] +fn check_errors(ra_fixture: &str, expect: Expect) { + let db = TestDB::with_files(ra_fixture); + let krate = db.fetch_test_crate(); + let def_map = db.crate_def_map(krate); + let errors = def_map + .modules() + .flat_map(|module| module.1.scope.all_macro_calls()) + .filter_map(|macro_call| { + let errors = db.parse_macro_expansion_error(macro_call)?; + let errors = errors.err.as_ref()?.render_to_string(&db); + let macro_loc = db.lookup_intern_macro_call(macro_call); + let ast_id = match macro_loc.kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.map(|it| it.erase()), + MacroCallKind::Derive { ast_id, .. } => ast_id.map(|it| it.erase()), + MacroCallKind::Attr { ast_id, .. } => ast_id.map(|it| it.erase()), + }; + let ast = db + .parse(ast_id.file_id.file_id().expect("macros inside macros are not supported")) + .syntax_node(); + let ast_id_map = db.ast_id_map(ast_id.file_id); + let node = ast_id_map.get_erased(ast_id.value).to_node(&ast); + Some((node.text_range(), errors)) + }) + .sorted_unstable_by_key(|(range, _)| range.start()) + .format_with("\n", |(range, err), format| format(&format_args!("{range:?}: {err}"))) + .to_string(); + expect.assert_eq(&errors); +} + +#[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let extra_proc_macros = vec![( r#" @@ -245,7 +277,9 @@ fn pretty_print_macro_expansion( let mut res = String::new(); let mut prev_kind = EOF; let mut indent_level = 0; - for token in iter::successors(expn.first_token(), |t| t.next_token()) { + for token in iter::successors(expn.first_token(), |t| t.next_token()) + .take_while(|token| token.text_range().start() < expn.text_range().end()) + { let curr_kind = token.kind(); let space = match (prev_kind, curr_kind) { _ if prev_kind.is_trivia() || curr_kind.is_trivia() => "", @@ -313,14 +347,14 @@ struct IdentityWhenValidProcMacroExpander; impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { fn expand( &self, - subtree: &Subtree, - _: Option<&Subtree>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &base_db::Env, _: Span, _: Span, _: Span, _: Option<String>, - ) -> Result<Subtree, ProcMacroExpansionError> { + ) -> Result<TopSubtree, ProcMacroExpansionError> { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, syntax_bridge::TopEntryPoint::MacroItems, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 9bd7d38f0a6..39d383f0159 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -85,6 +85,8 @@ use crate::{ FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; +pub use self::path_resolution::ResolvePathResultPrefixInfo; + const PREDEFINED_TOOLS: &[SmolStr] = &[ SmolStr::new_static("clippy"), SmolStr::new_static("rustfmt"), @@ -615,13 +617,15 @@ impl DefMap { (res.resolved_def, res.segment_index) } + /// The first `Option<usize>` points at the `Enum` segment in case of `Enum::Variant`, the second + /// points at the unresolved segments. pub(crate) fn resolve_path_locally( &self, db: &dyn DefDatabase, original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, - ) -> (PerNs, Option<usize>) { + ) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) { let res = self.resolve_path_fp_with_macro_single( db, ResolveMode::Other, @@ -630,7 +634,7 @@ impl DefMap { shadow, None, // Currently this function isn't used for macro resolution. ); - (res.resolved_def, res.segment_index) + (res.resolved_def, res.segment_index, res.prefix_info) } /// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 747860fd8e1..d1f6ed023c2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -110,8 +110,8 @@ pub(super) fn attr_macro_as_call_id( ) -> MacroCallId { let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { - let mut tt = tt.as_ref().clone(); - tt.delimiter.kind = tt::DelimiterKind::Invisible; + let mut tt = tt.clone(); + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; Some(tt) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index f391cc41c18..8beeda82bca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -353,7 +353,7 @@ impl DefCollector<'_> { let is_cfg_enabled = item_tree .top_level_attrs(self.db, self.def_map.krate) .cfg() - .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)); + .is_none_or(|cfg| self.cfg_options.check(&cfg) != Some(false)); if is_cfg_enabled { self.inject_prelude(); @@ -797,7 +797,7 @@ impl DefCollector<'_> { return PartialResolvedImport::Unresolved; } - if res.from_differing_crate { + if res.prefix_info.differing_crate { return PartialResolvedImport::Resolved( def.filter_visibility(|v| matches!(v, Visibility::Public)), ); @@ -1316,6 +1316,7 @@ impl DefCollector<'_> { // being cfg'ed out). // Ideally we will just expand them to nothing here. But we are only collecting macro calls, // not expanding them, so we have no way to do that. + // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`. if matches!( def.kind, MacroDefKind::BuiltInAttr(_, expander) @@ -1451,13 +1452,7 @@ impl DefCollector<'_> { depth: usize, container: ItemContainerId, ) { - let recursion_limit = self.def_map.recursion_limit() as usize; - let recursion_limit = Limit::new(if cfg!(test) { - // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug - std::cmp::min(32, recursion_limit) - } else { - recursion_limit - }); + let recursion_limit = Limit::new(self.def_map.recursion_limit() as usize); if recursion_limit.check(depth).is_err() { cov_mark::hit!(macro_expansion_overflow); tracing::warn!("macro expansion is too deep"); @@ -2220,8 +2215,8 @@ impl ModCollector<'_, '_> { let is_export = export_attr.exists(); let local_inner = if is_export { - export_attr.tt_values().flat_map(|it| it.token_trees.iter()).any(|it| match it { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros, + export_attr.tt_values().flat_map(|it| it.iter()).any(|it| match it { + tt::TtElement::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros, _ => false, }) } else { @@ -2240,7 +2235,7 @@ impl ModCollector<'_, '_> { None => { let explicit_name = attrs.by_key(&sym::rustc_builtin_macro).tt_values().next().and_then(|tt| { - match tt.token_trees.first() { + match tt.token_trees().flat_tokens().first() { Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name), _ => None, } @@ -2310,9 +2305,7 @@ impl ModCollector<'_, '_> { // NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`, // in which case rustc ignores the helper attributes from the latter, but it // "doesn't make sense in practice" (see rust-lang/rust#87027). - if let Some((name, helpers)) = - parse_macro_name_and_helper_attrs(&attr.token_trees) - { + if let Some((name, helpers)) = parse_macro_name_and_helper_attrs(attr) { // NOTE: rustc overrides the name if the macro name if it's different from the // macro name, but we assume it isn't as there's no such case yet. FIXME if // the following assertion fails. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 8eb195680d1..47c08d3d1dc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -43,21 +43,33 @@ pub(super) struct ResolvePathResult { pub(super) resolved_def: PerNs, pub(super) segment_index: Option<usize>, pub(super) reached_fixedpoint: ReachedFixedPoint, - pub(super) from_differing_crate: bool, + pub(super) prefix_info: ResolvePathResultPrefixInfo, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct ResolvePathResultPrefixInfo { + pub(crate) differing_crate: bool, + /// Path of the form `Enum::Variant` (and not `Variant` alone). + pub enum_variant: bool, } impl ResolvePathResult { fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { - ResolvePathResult::new(PerNs::none(), reached_fixedpoint, None, false) + ResolvePathResult::new( + PerNs::none(), + reached_fixedpoint, + None, + ResolvePathResultPrefixInfo::default(), + ) } fn new( resolved_def: PerNs, reached_fixedpoint: ReachedFixedPoint, segment_index: Option<usize>, - from_differing_crate: bool, + prefix_info: ResolvePathResultPrefixInfo, ) -> ResolvePathResult { - ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, from_differing_crate } + ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } } } @@ -157,7 +169,8 @@ impl DefMap { if result.reached_fixedpoint == ReachedFixedPoint::No { result.reached_fixedpoint = new.reached_fixedpoint; } - result.from_differing_crate |= new.from_differing_crate; + result.prefix_info.differing_crate |= new.prefix_info.differing_crate; + result.prefix_info.enum_variant |= new.prefix_info.enum_variant; result.segment_index = match (result.segment_index, new.segment_index) { (Some(idx), None) => Some(idx), (Some(old), Some(new)) => Some(old.max(new)), @@ -403,14 +416,14 @@ impl DefMap { fn resolve_remaining_segments<'a>( &self, - segments: impl Iterator<Item = (usize, &'a Name)>, + mut segments: impl Iterator<Item = (usize, &'a Name)>, mut curr_per_ns: PerNs, path: &ModPath, db: &dyn DefDatabase, shadow: BuiltinShadowMode, original_module: LocalModuleId, ) -> ResolvePathResult { - for (i, segment) in segments { + while let Some((i, segment)) = segments.next() { let curr = match curr_per_ns.take_types_full() { Some(r) => r, None => { @@ -437,13 +450,22 @@ impl DefMap { // because `macro_use` and other preludes should be taken into account. At // this point, we know we're resolving a multi-segment path so macro kind // expectation is discarded. - let (def, s) = - defp_map.resolve_path(db, module.local_id, &path, shadow, None); + let resolution = defp_map.resolve_path_fp_with_macro( + db, + ResolveMode::Other, + module.local_id, + &path, + shadow, + None, + ); return ResolvePathResult::new( - def, + resolution.resolved_def, ReachedFixedPoint::Yes, - s.map(|s| s + i), - true, + resolution.segment_index.map(|s| s + i), + ResolvePathResultPrefixInfo { + differing_crate: true, + enum_variant: resolution.prefix_info.enum_variant, + }, ); } @@ -488,17 +510,31 @@ impl DefMap { ), }) }); - match res { - Some(res) => res, - None => { - return ResolvePathResult::new( - PerNs::types(e.into(), curr.vis, curr.import), - ReachedFixedPoint::Yes, - Some(i), - false, - ) + // FIXME: Need to filter visibility here and below? Not sure. + return match res { + Some(res) => { + if segments.next().is_some() { + // Enum variants are in value namespace, segments left => no resolution. + ResolvePathResult::empty(ReachedFixedPoint::No) + } else { + ResolvePathResult::new( + res, + ReachedFixedPoint::Yes, + None, + ResolvePathResultPrefixInfo { + enum_variant: true, + ..ResolvePathResultPrefixInfo::default() + }, + ) + } } - } + None => ResolvePathResult::new( + PerNs::types(e.into(), curr.vis, curr.import), + ReachedFixedPoint::Yes, + Some(i), + ResolvePathResultPrefixInfo::default(), + ), + }; } s => { // could be an inherent method call in UFCS form @@ -513,7 +549,7 @@ impl DefMap { PerNs::types(s, curr.vis, curr.import), ReachedFixedPoint::Yes, Some(i), - false, + ResolvePathResultPrefixInfo::default(), ); } }; @@ -522,7 +558,12 @@ impl DefMap { .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); } - ResolvePathResult::new(curr_per_ns, ReachedFixedPoint::Yes, None, false) + ResolvePathResult::new( + curr_per_ns, + ReachedFixedPoint::Yes, + None, + ResolvePathResultPrefixInfo::default(), + ) } fn resolve_name_in_module( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index fd0b52bc7d7..b93a1c87b43 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -4,7 +4,7 @@ use hir_expand::name::{AsName, Name}; use intern::sym; use crate::attr::Attrs; -use crate::tt::{Leaf, TokenTree}; +use crate::tt::{Leaf, TokenTree, TopSubtree, TtElement}; #[derive(Debug, PartialEq, Eq)] pub struct ProcMacroDef { @@ -38,7 +38,7 @@ impl Attrs { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) } else if self.by_key(&sym::proc_macro_derive).exists() { let derive = self.by_key(&sym::proc_macro_derive).tt_values().next()?; - let def = parse_macro_name_and_helper_attrs(&derive.token_trees) + let def = parse_macro_name_and_helper_attrs(derive) .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }); if def.is_none() { @@ -55,8 +55,8 @@ impl Attrs { // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have // the same structure. #[rustfmt::skip] -pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> { - match tt { +pub(crate) fn parse_macro_name_and_helper_attrs(tt: &TopSubtree) -> Option<(Name, Box<[Name]>)> { + match tt.token_trees().flat_tokens() { // `#[proc_macro_derive(Trait)]` // `#[rustc_builtin_macro(Trait)]` [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))), @@ -67,17 +67,18 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Nam TokenTree::Leaf(Leaf::Ident(trait_name)), TokenTree::Leaf(Leaf::Punct(comma)), TokenTree::Leaf(Leaf::Ident(attributes)), - TokenTree::Subtree(helpers) + TokenTree::Subtree(_), + .. ] if comma.char == ',' && attributes.sym == sym::attributes => { + let helpers = tt::TokenTreesView::new(&tt.token_trees().flat_tokens()[3..]).try_into_subtree()?; let helpers = helpers - .token_trees .iter() .filter( - |tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','), + |tt| !matches!(tt, TtElement::Leaf(Leaf::Punct(comma)) if comma.char == ','), ) .map(|tt| match tt { - TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()), + TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()), _ => None, }) .collect::<Option<Box<[_]>>>()?; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs index 44e132061ad..e59c37104dd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs @@ -240,6 +240,7 @@ pub struct PathSegment<'a> { pub args_and_bindings: Option<&'a GenericArgs>, } +#[derive(Debug, Clone, Copy)] pub struct PathSegments<'a> { segments: &'a [Name], generic_args: Option<&'a [Option<GenericArgs>]>, @@ -259,6 +260,7 @@ impl<'a> PathSegments<'a> { pub fn last(&self) -> Option<PathSegment<'a>> { self.get(self.len().checked_sub(1)?) } + pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> { let res = PathSegment { name: self.segments.get(idx)?, @@ -266,24 +268,37 @@ impl<'a> PathSegments<'a> { }; Some(res) } + pub fn skip(&self, len: usize) -> PathSegments<'a> { PathSegments { segments: self.segments.get(len..).unwrap_or(&[]), generic_args: self.generic_args.and_then(|it| it.get(len..)), } } + pub fn take(&self, len: usize) -> PathSegments<'a> { PathSegments { segments: self.segments.get(..len).unwrap_or(self.segments), 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 strip_last_two(&self) -> PathSegments<'a> { + PathSegments { + segments: self.segments.get(..self.segments.len().saturating_sub(2)).unwrap_or(&[]), + generic_args: self + .generic_args + .map(|it| it.get(..it.len().saturating_sub(2)).unwrap_or(&[])), + } + } + pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> { self.segments .iter() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index f4dfd42a30e..82da57a9bb2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -21,7 +21,7 @@ use crate::{ hir::{BindingId, ExprId, LabelId}, item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, lang_item::LangItemTarget, - nameres::{DefMap, MacroSubNs}, + nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, per_ns::PerNs, type_ref::{LifetimeRef, TypesMap}, @@ -263,25 +263,37 @@ impl Resolver { &self, db: &dyn DefDatabase, path: &Path, - mut hygiene_id: HygieneId, + hygiene_id: HygieneId, ) -> Option<ResolveValueResult> { + self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it) + } + + pub fn resolve_path_in_value_ns_with_prefix_info( + &self, + db: &dyn DefDatabase, + path: &Path, + mut hygiene_id: HygieneId, + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => it.mod_path(), Path::LangItem(l, None) => { - return Some(ResolveValueResult::ValueNs( - match *l { - LangItemTarget::Function(it) => ValueNs::FunctionId(it), - LangItemTarget::Static(it) => ValueNs::StaticId(it), - LangItemTarget::Struct(it) => ValueNs::StructId(it), - LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), - LangItemTarget::Union(_) - | LangItemTarget::ImplDef(_) - | LangItemTarget::TypeAlias(_) - | LangItemTarget::Trait(_) - | LangItemTarget::EnumId(_) => return None, - }, - None, + return Some(( + ResolveValueResult::ValueNs( + match *l { + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + }, + None, + ), + ResolvePathResultPrefixInfo::default(), )) } Path::LangItem(l, Some(_)) => { @@ -296,7 +308,10 @@ impl Resolver { | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, }; - return Some(ResolveValueResult::Partial(type_ns, 1, None)); + return Some(( + ResolveValueResult::Partial(type_ns, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } }; let n_segments = path.segments().len(); @@ -326,9 +341,12 @@ impl Resolver { }); if let Some(e) = entry { - return Some(ResolveValueResult::ValueNs( - ValueNs::LocalBinding(e.binding()), - None, + return Some(( + ResolveValueResult::ValueNs( + ValueNs::LocalBinding(e.binding()), + None, + ), + ResolvePathResultPrefixInfo::default(), )); } } @@ -350,14 +368,17 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); - return Some(ResolveValueResult::ValueNs(val, None)); + return Some(( + ResolveValueResult::ValueNs(val, None), + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some(ResolveValueResult::ValueNs( - ValueNs::ImplSelf(impl_), - None, + return Some(( + ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None), + ResolvePathResultPrefixInfo::default(), )); } } @@ -377,22 +398,27 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); - return Some(ResolveValueResult::Partial(ty, 1, None)); + return Some(( + ResolveValueResult::Partial(ty, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some(ResolveValueResult::Partial( - TypeNs::SelfType(impl_), - 1, - None, + return Some(( + ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None), + ResolvePathResultPrefixInfo::default(), )); } } Scope::AdtScope(adt) => { if *first_name == sym::Self_.clone() { let ty = TypeNs::AdtSelfType(*adt); - return Some(ResolveValueResult::Partial(ty, 1, None)); + return Some(( + ResolveValueResult::Partial(ty, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } Scope::BlockScope(m) => { @@ -413,7 +439,10 @@ impl Resolver { // `use core::u16;`. if path.kind == PathKind::Plain && n_segments > 1 { if let Some(builtin) = BuiltinType::by_name(first_name) { - return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None)); + return Some(( + ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } @@ -924,15 +953,15 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option<ResolveValueResult> { - let (module_def, idx) = + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); - match idx { + match unresolved_idx { None => { let (value, import) = to_value_ns(module_def)?; - Some(ResolveValueResult::ValueNs(value, import)) + Some((ResolveValueResult::ValueNs(value, import), prefix_info)) } - Some(idx) => { + Some(unresolved_idx) => { let def = module_def.take_types_full()?; let ty = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), @@ -948,7 +977,7 @@ impl ModuleItemMap { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some(ResolveValueResult::Partial(ty, idx, def.import)) + Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info)) } } } @@ -958,7 +987,7 @@ impl ModuleItemMap { db: &dyn DefDatabase, path: &ModPath, ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { - let (module_def, idx) = + let (module_def, idx, _) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); let (res, import) = to_type_ns(module_def)?; Some((res, idx, import)) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 12df3cf2188..c9c793d54f2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -19,7 +19,7 @@ use crate::{ db::ExpandDatabase, mod_path::ModPath, span_map::SpanMapRef, - tt::{self, token_to_literal, Subtree}, + tt::{self, token_to_literal, TopSubtree}, InFile, }; @@ -107,8 +107,8 @@ impl RawAttrs { .chain(b.slice.iter().map(|it| { let mut it = it.clone(); it.id.id = (it.id.ast_index() as u32 + last_ast_index) - | (it.id.cfg_attr_index().unwrap_or(0) as u32) - << AttrId::AST_INDEX_BITS; + | ((it.id.cfg_attr_index().unwrap_or(0) as u32) + << AttrId::AST_INDEX_BITS); it })) .collect::<Vec<_>>(); @@ -122,7 +122,7 @@ impl RawAttrs { pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs { let has_cfg_attrs = self .iter() - .any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone())); + .any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone())); if !has_cfg_attrs { return self; } @@ -132,7 +132,7 @@ impl RawAttrs { self.iter() .flat_map(|attr| -> SmallVec<[_; 1]> { let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone()); + attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone()); if !is_cfg_attr { return smallvec![attr.clone()]; } @@ -152,7 +152,7 @@ impl RawAttrs { ); let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); let cfg = CfgExpr::parse(&cfg); if cfg_options.check(&cfg) == Some(false) { smallvec![] @@ -202,7 +202,7 @@ impl AttrId { } pub fn with_cfg_attr(self, idx: usize) -> AttrId { - AttrId { id: self.id | (idx as u32) << Self::AST_INDEX_BITS | Self::CFG_ATTR_SET_BITS } + AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS } } } @@ -219,7 +219,7 @@ pub enum AttrInput { /// `#[attr = "string"]` Literal(tt::Literal), /// `#[attr(subtree)]` - TokenTree(Box<tt::Subtree>), + TokenTree(tt::TopSubtree), } impl fmt::Display for AttrInput { @@ -254,46 +254,59 @@ impl Attr { span, DocCommentDesugarMode::ProcMacro, ); - Some(Box::new(AttrInput::TokenTree(Box::new(tree)))) + Some(Box::new(AttrInput::TokenTree(tree))) } else { None }; Some(Attr { id, path, input, ctxt: span.ctx }) } - fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> { - if matches!(tt, + fn from_tt( + db: &dyn ExpandDatabase, + mut tt: tt::TokenTreesView<'_>, + id: AttrId, + ) -> Option<Attr> { + if matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..] if *sym == sym::unsafe_ ) { - match tt.get(1) { - Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees, + match tt.iter().nth(1) { + Some(tt::TtElement::Subtree(_, iter)) => tt = iter.remaining(), _ => return None, } } - let first = &tt.first()?; + let first = tt.flat_tokens().first()?; let ctxt = first.first_span().ctx; - let path_end = tt - .iter() - .position(|tt| { - !matches!( + let (path, input) = { + let mut iter = tt.iter(); + let start = iter.savepoint(); + let mut input = tt::TokenTreesView::new(&[]); + let mut path = iter.from_savepoint(start); + let mut path_split_savepoint = iter.savepoint(); + while let Some(tt) = iter.next() { + path = iter.from_savepoint(start); + if !matches!( tt, - tt::TokenTree::Leaf( + tt::TtElement::Leaf( tt::Leaf::Punct(tt::Punct { char: ':' | '$', .. }) | tt::Leaf::Ident(_), ) - ) - }) - .unwrap_or(tt.len()); + ) { + input = path_split_savepoint.remaining(); + break; + } + path_split_savepoint = iter.savepoint(); + } + (path, input) + }; - let (path, input) = tt.split_at(path_end); let path = Interned::new(ModPath::from_tt(db, path)?); - let input = match input.first() { - Some(tt::TokenTree::Subtree(tree)) => { - Some(Box::new(AttrInput::TokenTree(Box::new(tree.clone())))) + let input = match (input.flat_tokens().first(), input.try_into_subtree()) { + (_, Some(tree)) => { + Some(Box::new(AttrInput::TokenTree(tt::TopSubtree::from_subtree(tree)))) } - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { - let input = match input.get(1) { + (Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))), _) => { + let input = match input.flat_tokens().get(1) { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { Some(Box::new(AttrInput::Literal(lit.clone()))) } @@ -352,7 +365,7 @@ impl Attr { /// #[path(ident)] pub fn single_ident_value(&self) -> Option<&tt::Ident> { match self.input.as_deref()? { - AttrInput::TokenTree(tt) => match &*tt.token_trees { + AttrInput::TokenTree(tt) => match tt.token_trees().flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident), _ => None, }, @@ -361,7 +374,7 @@ impl Attr { } /// #[path TokenTree] - pub fn token_tree_value(&self) -> Option<&Subtree> { + pub fn token_tree_value(&self) -> Option<&TopSubtree> { match self.input.as_deref()? { AttrInput::TokenTree(tt) => Some(tt), _ => None, @@ -375,14 +388,14 @@ impl Attr { ) -> Option<impl Iterator<Item = (ModPath, Span)> + 'a> { let args = self.token_tree_value()?; - if args.delimiter.kind != DelimiterKind::Parenthesis { + if args.top_subtree().delimiter.kind != DelimiterKind::Parenthesis { return None; } let paths = args - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) + .token_trees() + .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) .filter_map(move |tts| { - let span = tts.first()?.first_span(); + let span = tts.flat_tokens().first()?.first_span(); Some((ModPath::from_tt(db, tts)?, span)) }); @@ -467,11 +480,11 @@ fn inner_attributes( // Input subtree is: `(cfg, $(attr),+)` // Split it up into a `cfg` subtree and the `attr` subtrees. fn parse_cfg_attr_input( - subtree: &Subtree, -) -> Option<(&[tt::TokenTree], impl Iterator<Item = &[tt::TokenTree]>)> { + subtree: &TopSubtree, +) -> Option<(tt::TokenTreesView<'_>, impl Iterator<Item = tt::TokenTreesView<'_>>)> { let mut parts = subtree - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))); + .token_trees() + .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))); let cfg = parts.next()?; Some((cfg, parts.filter(|it| !it.is_empty()))) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 74effd2fb16..f250620e775 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -14,7 +14,7 @@ macro_rules! register_builtin { } impl BuiltinAttrExpander { - pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> { + pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult<tt::TopSubtree> { match *self { $( BuiltinAttrExpander::$variant => $expand, )* } @@ -36,9 +36,9 @@ impl BuiltinAttrExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { self.expander()(db, id, tt, span) } @@ -75,18 +75,18 @@ pub fn find_builtin_attr(ident: &name::Name) -> Option<BuiltinAttrExpander> { fn dummy_attr_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, _span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { ExpandResult::ok(tt.clone()) } fn dummy_gate_test_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let result = quote::quote! { span=> #[cfg(test)] #tt @@ -118,47 +118,41 @@ fn dummy_gate_test_expand( fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let loc = db.lookup_intern_macro_call(id); let derives = match &loc.kind { MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => { attr_args } _ => { - return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span })) + return ExpandResult::ok(tt::TopSubtree::empty(tt::DelimSpan { + open: span, + close: span, + })) } }; pseudo_derive_attr_expansion(tt, derives, span) } pub fn pseudo_derive_attr_expansion( - _: &tt::Subtree, - args: &tt::Subtree, + _: &tt::TopSubtree, + args: &tt::TopSubtree, call_site: Span, -) -> ExpandResult<tt::Subtree> { - let mk_leaf = |char| { - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char, - spacing: tt::Spacing::Alone, - span: call_site, - })) - }; +) -> ExpandResult<tt::TopSubtree> { + let mk_leaf = + |char| tt::Leaf::Punct(tt::Punct { char, spacing: tt::Spacing::Alone, span: call_site }); - let mut token_trees = Vec::new(); - for tt in args - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. })))) - { - token_trees.push(mk_leaf('#')); - token_trees.push(mk_leaf('!')); - token_trees.push(mk_leaf('[')); - token_trees.extend(tt.iter().cloned()); - token_trees.push(mk_leaf(']')); + let mut token_trees = tt::TopSubtreeBuilder::new(args.top_subtree().delimiter); + let iter = args.token_trees().split(|tt| { + matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) + }); + for tts in iter { + token_trees.extend([mk_leaf('#'), mk_leaf('!')]); + token_trees.open(tt::DelimiterKind::Bracket, call_site); + token_trees.extend_with_tt(tts); + token_trees.close(call_site); } - ExpandResult::ok(tt::Subtree { - delimiter: args.delimiter, - token_trees: token_trees.into_boxed_slice(), - }) + ExpandResult::ok(token_trees.build()) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 7d3e8deaf08..4510a593af4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1,9 +1,10 @@ //! Builtin derives. use intern::sym; -use itertools::izip; +use itertools::{izip, Itertools}; +use parser::SyntaxKind; use rustc_hash::FxHashSet; -use span::{MacroCallId, Span}; +use span::{MacroCallId, Span, SyntaxContextId}; use stdx::never; use syntax_bridge::DocCommentDesugarMode; use tracing::debug; @@ -16,8 +17,12 @@ use crate::{ span_map::ExpansionSpanMap, tt, ExpandError, ExpandResult, }; -use syntax::ast::{ - self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, +use syntax::{ + ast::{ + self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, FieldList, HasAttrs, + HasGenericArgs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, + }, + ted, }; macro_rules! register_builtin { @@ -28,7 +33,7 @@ macro_rules! register_builtin { } impl BuiltinDeriveExpander { - pub fn expander(&self) -> fn(Span, &tt::Subtree) -> ExpandResult<tt::Subtree> { + pub fn expander(&self) -> fn(Span, &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { match *self { $( BuiltinDeriveExpander::$trait => $expand, )* } @@ -50,9 +55,9 @@ impl BuiltinDeriveExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { let span = span_with_def_site_ctxt(db, span, id); self.expander()(span, tt) } @@ -67,13 +72,15 @@ register_builtin! { Ord => ord_expand, PartialOrd => partial_ord_expand, Eq => eq_expand, - PartialEq => partial_eq_expand + PartialEq => partial_eq_expand, + CoercePointee => coerce_pointee_expand } pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> { BuiltinDeriveExpander::find_by_name(ident) } +#[derive(Clone)] enum VariantShape { Struct(Vec<tt::Ident>), Tuple(usize), @@ -85,7 +92,7 @@ fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator<Item = tt::Ident> } impl VariantShape { - fn as_pattern(&self, path: tt::Subtree, span: Span) -> tt::Subtree { + fn as_pattern(&self, path: tt::TopSubtree, span: Span) -> tt::TopSubtree { self.as_pattern_map(path, span, |it| quote!(span => #it)) } @@ -99,10 +106,10 @@ impl VariantShape { fn as_pattern_map( &self, - path: tt::Subtree, + path: tt::TopSubtree, span: Span, - field_map: impl Fn(&tt::Ident) -> tt::Subtree, - ) -> tt::Subtree { + field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, + ) -> tt::TopSubtree { match self { VariantShape::Struct(fields) => { let fields = fields.iter().map(|it| { @@ -147,6 +154,7 @@ impl VariantShape { } } +#[derive(Clone)] enum AdtShape { Struct(VariantShape), Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> }, @@ -154,7 +162,7 @@ enum AdtShape { } impl AdtShape { - fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec<tt::Subtree> { + fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec<tt::TopSubtree> { self.as_pattern_map(name, |it| quote!(span =>#it), span) } @@ -176,9 +184,9 @@ impl AdtShape { fn as_pattern_map( &self, name: &tt::Ident, - field_map: impl Fn(&tt::Ident) -> tt::Subtree, + field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, span: Span, - ) -> Vec<tt::Subtree> { + ) -> Vec<tt::TopSubtree> { match self { AdtShape::Struct(s) => { vec![s.as_pattern_map(quote! {span => #name }, span, field_map)] @@ -197,30 +205,38 @@ impl AdtShape { } } +#[derive(Clone)] struct BasicAdtInfo { name: tt::Ident, shape: AdtShape, /// first field is the name, and /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. /// third fields is where bounds, if any - param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>, - where_clause: Vec<tt::Subtree>, - associated_types: Vec<tt::Subtree>, + param_types: Vec<AdtParam>, + where_clause: Vec<tt::TopSubtree>, + associated_types: Vec<tt::TopSubtree>, } -fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> { - let (parsed, tm) = &syntax_bridge::token_tree_to_syntax_node( - tt, - syntax_bridge::TopEntryPoint::MacroItems, - parser::Edition::CURRENT_FIXME, - ); - let macro_items = ast::MacroItems::cast(parsed.syntax_node()) - .ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?; - let item = - macro_items.items().next().ok_or_else(|| ExpandError::other(call_site, "no item found"))?; - let adt = &ast::Adt::cast(item.syntax().clone()) - .ok_or_else(|| ExpandError::other(call_site, "expected struct, enum or union"))?; - let (name, generic_param_list, where_clause, shape) = match adt { +#[derive(Clone)] +struct AdtParam { + name: tt::TopSubtree, + /// `None` if this is a type parameter. + const_ty: Option<tt::TopSubtree>, + bounds: Option<tt::TopSubtree>, +} + +// FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess. +fn parse_adt(tt: &tt::TopSubtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> { + let (adt, tm) = to_adt_syntax(tt, call_site)?; + parse_adt_from_syntax(&adt, &tm, call_site) +} + +fn parse_adt_from_syntax( + adt: &ast::Adt, + tm: &span::SpanMap<SyntaxContextId>, + call_site: Span, +) -> Result<BasicAdtInfo, ExpandError> { + let (name, generic_param_list, where_clause, shape) = match &adt { ast::Adt::Struct(it) => ( it.name(), it.generic_param_list(), @@ -276,7 +292,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr ) } None => { - tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) + tt::TopSubtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) } } }; @@ -291,7 +307,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr }), ast::TypeOrConstParam::Const(_) => None, }; - let ty = if let ast::TypeOrConstParam::Const(param) = param { + let const_ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() .map(|ty| { @@ -303,13 +319,13 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr ) }) .unwrap_or_else(|| { - tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) + tt::TopSubtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) }); Some(ty) } else { None }; - (name, ty, bounds) + AdtParam { name, const_ty, bounds } }) .collect(); @@ -365,6 +381,24 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr Ok(BasicAdtInfo { name: name_token, shape, param_types, where_clause, associated_types }) } +fn to_adt_syntax( + tt: &tt::TopSubtree, + call_site: Span, +) -> Result<(ast::Adt, span::SpanMap<SyntaxContextId>), ExpandError> { + let (parsed, tm) = syntax_bridge::token_tree_to_syntax_node( + tt, + syntax_bridge::TopEntryPoint::MacroItems, + parser::Edition::CURRENT_FIXME, + ); + let macro_items = ast::MacroItems::cast(parsed.syntax_node()) + .ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?; + let item = + macro_items.items().next().ok_or_else(|| ExpandError::other(call_site, "no item found"))?; + let adt = ast::Adt::cast(item.syntax().clone()) + .ok_or_else(|| ExpandError::other(call_site, "expected struct, enum or union"))?; + Ok((adt, tm)) +} + fn name_to_token( call_site: Span, token_map: &ExpansionSpanMap, @@ -413,59 +447,85 @@ fn name_to_token( /// therefore does not get bound by the derived trait. fn expand_simple_derive( invoc_span: Span, - tt: &tt::Subtree, - trait_path: tt::Subtree, - make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree, -) -> ExpandResult<tt::Subtree> { + tt: &tt::TopSubtree, + trait_path: tt::TopSubtree, + make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, +) -> ExpandResult<tt::TopSubtree> { let info = match parse_adt(tt, invoc_span) { Ok(info) => info, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }), + tt::TopSubtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }), e, ) } }; + ExpandResult::ok(expand_simple_derive_with_parsed( + invoc_span, + info, + trait_path, + make_trait_body, + true, + tt::TopSubtree::empty(tt::DelimSpan::from_single(invoc_span)), + )) +} + +fn expand_simple_derive_with_parsed( + invoc_span: Span, + info: BasicAdtInfo, + trait_path: tt::TopSubtree, + make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, + constrain_to_trait: bool, + extra_impl_params: tt::TopSubtree, +) -> tt::TopSubtree { let trait_body = make_trait_body(&info); let mut where_block: Vec<_> = info.where_clause.into_iter().map(|w| quote! {invoc_span => #w , }).collect(); let (params, args): (Vec<_>, Vec<_>) = info .param_types .into_iter() - .map(|(ident, param_ty, bound)| { - let ident_ = ident.clone(); - if let Some(b) = bound { - let ident = ident.clone(); - where_block.push(quote! {invoc_span => #ident : #b , }); - } - if let Some(ty) = param_ty { - (quote! {invoc_span => const #ident : #ty , }, quote! {invoc_span => #ident_ , }) + .map(|param| { + let ident = param.name; + if let Some(b) = param.bounds { + let ident2 = ident.clone(); + where_block.push(quote! {invoc_span => #ident2 : #b , }); + } + if let Some(ty) = param.const_ty { + let ident2 = ident.clone(); + (quote! {invoc_span => const #ident : #ty , }, quote! {invoc_span => #ident2 , }) } else { let bound = trait_path.clone(); - (quote! {invoc_span => #ident : #bound , }, quote! {invoc_span => #ident_ , }) + let ident2 = ident.clone(); + let param = if constrain_to_trait { + quote! {invoc_span => #ident : #bound , } + } else { + quote! {invoc_span => #ident , } + }; + (param, quote! {invoc_span => #ident2 , }) } }) .unzip(); - where_block.extend(info.associated_types.iter().map(|it| { - let it = it.clone(); - let bound = trait_path.clone(); - quote! {invoc_span => #it : #bound , } - })); + if constrain_to_trait { + where_block.extend(info.associated_types.iter().map(|it| { + let it = it.clone(); + let bound = trait_path.clone(); + quote! {invoc_span => #it : #bound , } + })); + } let name = info.name; - let expanded = quote! {invoc_span => - impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body } - }; - ExpandResult::ok(expanded) + quote! {invoc_span => + impl < ##params #extra_impl_params > #trait_path for #name < ##args > where ##where_block { #trait_body } + } } -fn copy_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn copy_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) } -fn clone_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn clone_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -505,18 +565,18 @@ fn clone_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { } /// This function exists since `quote! {span => => }` doesn't work. -fn fat_arrow(span: Span) -> tt::Subtree { +fn fat_arrow(span: Span) -> tt::TopSubtree { let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span }; quote! {span => #eq> } } /// This function exists since `quote! {span => && }` doesn't work. -fn and_and(span: Span) -> tt::Subtree { +fn and_and(span: Span) -> tt::TopSubtree { let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span }; quote! {span => #and& } } -fn default_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::default::Default }, |adt| { let body = match &adt.shape { @@ -555,7 +615,7 @@ fn default_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { }) } -fn debug_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { @@ -627,7 +687,7 @@ fn debug_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { }) } -fn hash_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -674,12 +734,12 @@ fn hash_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { }) } -fn eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) } -fn partial_eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn partial_eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -731,7 +791,7 @@ fn self_and_other_patterns( adt: &BasicAdtInfo, name: &tt::Ident, span: Span, -) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) { +) -> (Vec<tt::TopSubtree>, Vec<tt::TopSubtree>) { let self_patterns = adt.shape.as_pattern_map( name, |it| { @@ -751,16 +811,16 @@ fn self_and_other_patterns( (self_patterns, other_patterns) } -fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::Ord }, |adt| { fn compare( krate: &tt::Ident, - left: tt::Subtree, - right: tt::Subtree, - rest: tt::Subtree, + left: tt::TopSubtree, + right: tt::TopSubtree, + rest: tt::TopSubtree, span: Span, - ) -> tt::Subtree { + ) -> tt::TopSubtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); quote! {span => @@ -809,16 +869,16 @@ fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { }) } -fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { +fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { fn compare( krate: &tt::Ident, - left: tt::Subtree, - right: tt::Subtree, - rest: tt::Subtree, + left: tt::TopSubtree, + right: tt::TopSubtree, + rest: tt::TopSubtree, span: Span, - ) -> tt::Subtree { + ) -> tt::TopSubtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); quote! {span => @@ -871,3 +931,493 @@ fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> } }) } + +fn coerce_pointee_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> { + let (adt, _span_map) = match to_adt_syntax(tt, span) { + Ok(it) => it, + Err(err) => { + return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err); + } + }; + let adt = adt.clone_for_update(); + let ast::Adt::Struct(strukt) = &adt else { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "`CoercePointee` can only be derived on `struct`s"), + ); + }; + let has_at_least_one_field = strukt + .field_list() + .map(|it| match it { + ast::FieldList::RecordFieldList(it) => it.fields().next().is_some(), + ast::FieldList::TupleFieldList(it) => it.fields().next().is_some(), + }) + .unwrap_or(false); + if !has_at_least_one_field { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s with at least one field", + ), + ); + } + let is_repr_transparent = strukt.attrs().any(|attr| { + attr.as_simple_call().is_some_and(|(name, tt)| { + name == "repr" + && tt.syntax().children_with_tokens().any(|it| { + it.into_token().is_some_and(|it| { + it.kind() == SyntaxKind::IDENT && it.text() == "transparent" + }) + }) + }) + }); + if !is_repr_transparent { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", + ), + ); + } + let type_params = strukt + .generic_param_list() + .into_iter() + .flat_map(|generics| { + generics.generic_params().filter_map(|param| match param { + ast::GenericParam::TypeParam(param) => Some(param), + _ => None, + }) + }) + .collect_vec(); + if type_params.is_empty() { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s that are generic over at least one type", + ), + ); + } + let (pointee_param, pointee_param_idx) = if type_params.len() == 1 { + // Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such. + (type_params[0].clone(), 0) + } else { + let mut pointees = type_params.iter().cloned().enumerate().filter(|(_, param)| { + param.attrs().any(|attr| { + let is_pointee = attr.as_simple_atom().is_some_and(|name| name == "pointee"); + if is_pointee { + // Remove the `#[pointee]` attribute so it won't be present in the generated + // impls (where we cannot resolve it). + ted::remove(attr.syntax()); + } + is_pointee + }) + }); + match (pointees.next(), pointees.next()) { + (Some((pointee_idx, pointee)), None) => (pointee, pointee_idx), + (None, _) => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "exactly one generic type parameter must be marked \ + as `#[pointee]` to derive `CoercePointee` traits", + ), + ) + } + (Some(_), Some(_)) => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "only one type parameter can be marked as `#[pointee]` \ + when deriving `CoercePointee` traits", + ), + ) + } + } + }; + let (Some(struct_name), Some(pointee_param_name)) = (strukt.name(), pointee_param.name()) + else { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "invalid item"), + ); + }; + + { + let mut pointee_has_maybe_sized_bound = false; + if let Some(bounds) = pointee_param.type_bound_list() { + pointee_has_maybe_sized_bound |= bounds.bounds().any(is_maybe_sized_bound); + } + if let Some(where_clause) = strukt.where_clause() { + pointee_has_maybe_sized_bound |= where_clause.predicates().any(|pred| { + let Some(ast::Type::PathType(ty)) = pred.ty() else { return false }; + let is_not_pointee = ty.path().is_none_or(|path| { + let is_pointee = path + .as_single_name_ref() + .is_some_and(|name| name.text() == pointee_param_name.text()); + !is_pointee + }); + if is_not_pointee { + return false; + } + pred.type_bound_list() + .is_some_and(|bounds| bounds.bounds().any(is_maybe_sized_bound)) + }) + } + if !pointee_has_maybe_sized_bound { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + format!("`derive(CoercePointee)` requires `{pointee_param_name}` to be marked `?Sized`"), + ), + ); + } + } + + const ADDED_PARAM: &str = "__S"; + + let where_clause = strukt.get_or_create_where_clause(); + + { + let mut new_predicates = Vec::new(); + + // # Rewrite generic parameter bounds + // For each bound `U: ..` in `struct<U: ..>`, make a new bound with `__S` in place of `#[pointee]` + // Example: + // ``` + // struct< + // U: Trait<T>, + // #[pointee] T: Trait<T> + ?Sized, + // V: Trait<T>> ... + // ``` + // ... generates this `impl` generic parameters + // ``` + // impl< + // U: Trait<T>, + // T: Trait<T> + ?Sized, + // V: Trait<T> + // > + // where + // U: Trait<__S>, + // __S: Trait<__S> + ?Sized, + // V: Trait<__S> ... + // ``` + for param in &type_params { + let Some(param_name) = param.name() else { continue }; + if let Some(bounds) = param.type_bound_list() { + // If the target type is the pointee, duplicate the bound as whole. + // Otherwise, duplicate only bounds that mention the pointee. + let is_pointee = param_name.text() == pointee_param_name.text(); + let new_bounds = bounds + .bounds() + .map(|bound| bound.clone_subtree().clone_for_update()) + .filter(|bound| { + bound.ty().is_some_and(|ty| { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) + || is_pointee + }) + }); + let new_bounds_target = if is_pointee { + make::name_ref(ADDED_PARAM) + } else { + make::name_ref(¶m_name.text()) + }; + new_predicates.push( + make::where_pred( + make::ty_path(make::path_from_segments( + [make::path_segment(new_bounds_target)], + false, + )), + new_bounds, + ) + .clone_for_update(), + ); + } + } + + // # Rewrite `where` clauses + // + // Move on to `where` clauses. + // Example: + // ``` + // struct MyPointer<#[pointee] T, ..> + // where + // U: Trait<V> + Trait<T>, + // Companion<T>: Trait<T>, + // T: Trait<T> + ?Sized, + // { .. } + // ``` + // ... will have a impl prelude like so + // ``` + // impl<..> .. + // where + // U: Trait<V> + Trait<T>, + // U: Trait<__S>, + // Companion<T>: Trait<T>, + // Companion<__S>: Trait<__S>, + // T: Trait<T> + ?Sized, + // __S: Trait<__S> + ?Sized, + // ``` + // + // We should also write a few new `where` bounds from `#[pointee] T` to `__S` + // as well as any bound that indirectly involves the `#[pointee] T` type. + for predicate in where_clause.predicates() { + let predicate = predicate.clone_subtree().clone_for_update(); + let Some(pred_target) = predicate.ty() else { continue }; + + // If the target type references the pointee, duplicate the bound as whole. + // Otherwise, duplicate only bounds that mention the pointee. + if substitute_type_in_bound( + pred_target.clone(), + &pointee_param_name.text(), + ADDED_PARAM, + ) { + if let Some(bounds) = predicate.type_bound_list() { + for bound in bounds.bounds() { + if let Some(ty) = bound.ty() { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM); + } + } + } + + new_predicates.push(predicate); + } else if let Some(bounds) = predicate.type_bound_list() { + let new_bounds = bounds + .bounds() + .map(|bound| bound.clone_subtree().clone_for_update()) + .filter(|bound| { + bound.ty().is_some_and(|ty| { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) + }) + }); + new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update()); + } + } + + for new_predicate in new_predicates { + where_clause.add_predicate(new_predicate); + } + } + + { + // # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location + // + // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. + where_clause.add_predicate( + make::where_pred( + make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(&pointee_param_name.text()))], + false, + )), + [make::type_bound(make::ty_path(make::path_from_segments( + [ + make::path_segment(make::name_ref("core")), + make::path_segment(make::name_ref("marker")), + make::generic_ty_path_segment( + make::name_ref("Unsize"), + [make::type_arg(make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(ADDED_PARAM))], + false, + ))) + .into()], + ), + ], + true, + )))], + ) + .clone_for_update(), + ); + } + + let self_for_traits = { + // Replace the `#[pointee]` with `__S`. + let mut type_param_idx = 0; + let self_params_for_traits = strukt + .generic_param_list() + .into_iter() + .flat_map(|params| params.generic_params()) + .filter_map(|param| { + Some(match param { + ast::GenericParam::ConstParam(param) => { + ast::GenericArg::ConstArg(make::expr_const_value(¶m.name()?.text())) + } + ast::GenericParam::LifetimeParam(param) => { + make::lifetime_arg(param.lifetime()?).into() + } + ast::GenericParam::TypeParam(param) => { + let name = if pointee_param_idx == type_param_idx { + make::name_ref(ADDED_PARAM) + } else { + make::name_ref(¶m.name()?.text()) + }; + type_param_idx += 1; + make::type_arg(make::ty_path(make::path_from_segments( + [make::path_segment(name)], + false, + ))) + .into() + } + }) + }); + let self_for_traits = make::path_from_segments( + [make::generic_ty_path_segment( + make::name_ref(&struct_name.text()), + self_params_for_traits, + )], + false, + ) + .clone_for_update(); + self_for_traits + }; + + let mut span_map = span::SpanMap::empty(); + // One span for them all. + span_map.push(adt.syntax().text_range().end(), span); + + let self_for_traits = syntax_bridge::syntax_node_to_token_tree( + self_for_traits.syntax(), + &span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + let info = match parse_adt_from_syntax(&adt, &span_map, span) { + Ok(it) => it, + Err(err) => { + return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err) + } + }; + + let self_for_traits2 = self_for_traits.clone(); + let krate = dollar_crate(span); + let krate2 = krate.clone(); + let dispatch_from_dyn = expand_simple_derive_with_parsed( + span, + info.clone(), + quote! {span => #krate2::ops::DispatchFromDyn<#self_for_traits2> }, + |_adt| quote! {span => }, + false, + quote! {span => __S }, + ); + let coerce_unsized = expand_simple_derive_with_parsed( + span, + info, + quote! {span => #krate::ops::CoerceUnsized<#self_for_traits> }, + |_adt| quote! {span => }, + false, + quote! {span => __S }, + ); + return ExpandResult::ok(quote! {span => #dispatch_from_dyn #coerce_unsized }); + + fn is_maybe_sized_bound(bound: ast::TypeBound) -> bool { + if bound.question_mark_token().is_none() { + return false; + } + let Some(ast::Type::PathType(ty)) = bound.ty() else { + return false; + }; + let Some(path) = ty.path() else { + return false; + }; + return segments_eq(&path, &["Sized"]) + || segments_eq(&path, &["core", "marker", "Sized"]) + || segments_eq(&path, &["std", "marker", "Sized"]); + + fn segments_eq(path: &ast::Path, expected: &[&str]) -> bool { + path.segments().zip_longest(expected.iter().copied()).all(|value| { + value.both().is_some_and(|(segment, expected)| { + segment.name_ref().is_some_and(|name| name.text() == expected) + }) + }) + } + } + + /// Returns true if any substitution was performed. + fn substitute_type_in_bound(ty: ast::Type, param_name: &str, replacement: &str) -> bool { + return match ty { + ast::Type::ArrayType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::DynTraitType(ty) => go_bounds(ty.type_bound_list(), param_name, replacement), + ast::Type::FnPtrType(ty) => any_long( + ty.param_list() + .into_iter() + .flat_map(|params| params.params().filter_map(|param| param.ty())) + .chain(ty.ret_type().and_then(|it| it.ty())), + |ty| substitute_type_in_bound(ty, param_name, replacement), + ), + ast::Type::ForType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::ImplTraitType(ty) => { + go_bounds(ty.type_bound_list(), param_name, replacement) + } + ast::Type::ParenType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::PathType(ty) => ty.path().is_some_and(|path| { + if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { + ted::replace( + path.syntax(), + make::path_from_segments( + [make::path_segment(make::name_ref(replacement))], + false, + ) + .clone_for_update() + .syntax(), + ); + return true; + } + + any_long( + path.segments() + .filter_map(|segment| segment.generic_arg_list()) + .flat_map(|it| it.generic_args()) + .filter_map(|generic_arg| match generic_arg { + ast::GenericArg::TypeArg(ty) => ty.ty(), + _ => None, + }), + |ty| substitute_type_in_bound(ty, param_name, replacement), + ) + }), + ast::Type::PtrType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::RefType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::SliceType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::TupleType(ty) => { + any_long(ty.fields(), |ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, + }; + + fn go_bounds( + bounds: Option<ast::TypeBoundList>, + param_name: &str, + replacement: &str, + ) -> bool { + bounds.is_some_and(|bounds| { + any_long(bounds.bounds(), |bound| { + bound + .ty() + .is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + }) + }) + } + + /// Like [`Iterator::any()`], but not short-circuiting. + fn any_long<I: Iterator, F: FnMut(I::Item) -> bool>(iter: I, mut f: F) -> bool { + let mut result = false; + iter.for_each(|item| result |= f(item)); + result + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index b76db2e0052..5b06de98757 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -14,12 +14,12 @@ use syntax::{ use syntax_bridge::syntax_node_to_token_tree; use crate::{ - builtin::quote::{dollar_crate, quote}, + builtin::quote::{dollar_crate, quote, WithDelimiter}, db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name, span_map::SpanMap, - tt::{self, DelimSpan}, + tt::{self, DelimSpan, TtElement, TtIter}, ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId, }; @@ -36,7 +36,7 @@ macro_rules! register_builtin { } impl BuiltinFnLikeExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> { + fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult<tt::TopSubtree> { match *self { $( BuiltinFnLikeExpander::$kind => $expand, )* } @@ -44,7 +44,7 @@ macro_rules! register_builtin { } impl EagerExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> { + fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult<tt::TopSubtree> { match *self { $( EagerExpander::$e_kind => $e_expand, )* } @@ -66,9 +66,9 @@ impl BuiltinFnLikeExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -83,9 +83,9 @@ impl EagerExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -146,24 +146,16 @@ register_builtin! { (option_env, OptionEnv) => option_env_expand } -fn mk_pound(span: Span) -> tt::Subtree { - crate::builtin::quote::IntoTt::to_subtree( - vec![crate::tt::Leaf::Punct(crate::tt::Punct { - char: '#', - spacing: crate::tt::Spacing::Alone, - span, - }) - .into()], - span, - ) +fn mk_pound(span: Span) -> tt::Leaf { + crate::tt::Leaf::Punct(crate::tt::Punct { char: '#', spacing: crate::tt::Spacing::Alone, span }) } fn module_path_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { // Just return a dummy result. ExpandResult::ok(quote! {span => "module::path" @@ -173,48 +165,48 @@ fn module_path_expand( fn line_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { // dummy implementation for type-checking purposes // Note that `line!` and `column!` will never be implemented properly, as they are by definition // not incremental - ExpandResult::ok(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + ExpandResult::ok(tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: sym::INTEGER_0.clone(), span, kind: tt::LitKind::Integer, suffix: Some(sym::u32.clone()), - }))]), - }) + })], + )) } fn log_syntax_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { ExpandResult::ok(quote! {span =>}) } fn trace_macros_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { ExpandResult::ok(quote! {span =>}) } fn stringify_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { - let pretty = ::tt::pretty(&tt.token_trees); +) -> ExpandResult<tt::TopSubtree> { + let pretty = ::tt::pretty(tt.token_trees().flat_tokens()); let expanded = quote! {span => #pretty @@ -226,39 +218,35 @@ fn stringify_expand( fn assert_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let call_site_span = span_with_call_site_ctxt(db, span, id); - let mut iter = ::tt::iter::TtIter::new(tt); + let mut iter = tt.iter(); let cond = expect_fragment( &mut iter, parser::PrefixEntryPoint::Expr, db.crate_graph()[id.lookup(db).krate].edition, - tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close }, + tt.top_subtree().delimiter.delim_span(), ); _ = iter.expect_char(','); - let rest = iter.as_slice(); + let rest = iter.remaining(); let dollar_crate = dollar_crate(span); - let expanded = match cond.value { - Some(cond) => { - let panic_args = rest.iter().cloned(); - let mac = if use_panic_2021(db, span) { - quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } - } else { - quote! {call_site_span => #dollar_crate::panic!(##panic_args) } - }; - quote! {call_site_span =>{ - if !(#cond) { - #mac; - } - }} - } - None => quote! {call_site_span =>{}}, + let panic_args = rest.iter(); + let mac = if use_panic_2021(db, span) { + quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } + } else { + quote! {call_site_span => #dollar_crate::panic!(##panic_args) } }; + let value = cond.value; + let expanded = quote! {call_site_span =>{ + if !(#value) { + #mac; + } + }}; match cond.err { Some(err) => ExpandResult::new(expanded, err.into()), @@ -269,9 +257,9 @@ fn assert_expand( fn file_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { // FIXME: RA purposefully lacks knowledge of absolute file names // so just return "". let file_name = "file"; @@ -286,12 +274,12 @@ fn file_expand( fn format_args_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let pound = mk_pound(span); let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; ExpandResult::ok(quote! {span => builtin #pound format_args #tt }) @@ -300,17 +288,17 @@ fn format_args_expand( fn format_args_nl_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let pound = mk_pound(span); let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. - }))) = tt.token_trees.first_mut() + }))) = tt.0.get_mut(1) { *text = Symbol::intern(&format_smolstr!("{}\\n", text.as_str())); } @@ -322,11 +310,11 @@ fn format_args_nl_expand( fn asm_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; let pound = mk_pound(span); let expanded = quote! {span => builtin #pound asm #tt @@ -337,9 +325,9 @@ fn asm_expand( fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let loc = db.lookup_intern_macro_call(id); let expr = CfgExpr::parse(tt); let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false); @@ -350,9 +338,9 @@ fn cfg_expand( fn panic_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); @@ -362,19 +350,18 @@ fn panic_expand( sym::panic_2015.clone() }; - // Expand to a macro call `$crate::panic::panic_{edition}` - let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); - // Pass the original arguments - let mut subtree = tt.clone(); - subtree.delimiter = tt::Delimiter { - open: call_site_span, - close: call_site_span, - kind: tt::DelimiterKind::Parenthesis, + let subtree = WithDelimiter { + delimiter: tt::Delimiter { + open: call_site_span, + close: call_site_span, + kind: tt::DelimiterKind::Parenthesis, + }, + token_trees: tt.token_trees(), }; - // FIXME(slow): quote! have a way to expand to builder to make this a vec! - call.push(tt::TokenTree::Subtree(subtree)); + // Expand to a macro call `$crate::panic::panic_{edition}` + let call = quote!(call_site_span =>#dollar_crate::panic::#mac! #subtree); ExpandResult::ok(call) } @@ -382,9 +369,9 @@ fn panic_expand( fn unreachable_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); @@ -394,19 +381,16 @@ fn unreachable_expand( sym::unreachable_2015.clone() }; - // Expand to a macro call `$crate::panic::panic_{edition}` - let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); - // Pass the original arguments let mut subtree = tt.clone(); - subtree.delimiter = tt::Delimiter { + *subtree.top_subtree_delimiter_mut() = tt::Delimiter { open: call_site_span, close: call_site_span, kind: tt::DelimiterKind::Parenthesis, }; - // FIXME(slow): quote! have a way to expand to builder to make this a vec! - call.push(tt::TokenTree::Subtree(subtree)); + // Expand to a macro call `$crate::panic::panic_{edition}` + let call = quote!(call_site_span =>#dollar_crate::panic::#mac! #subtree); ExpandResult::ok(call) } @@ -436,11 +420,11 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { fn compile_error_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { - let err = match &*tt.token_trees { - [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { +) -> ExpandResult<tt::TopSubtree> { + let err = match &*tt.0 { + [_, tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span: _, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), @@ -455,9 +439,9 @@ fn compile_error_expand( fn concat_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let mut err = None; let mut text = String::new(); let mut span: Option<Span> = None; @@ -466,19 +450,19 @@ fn concat_expand( Some(_) => (), None => span = Some(s), }; - for (i, mut t) in tt.token_trees.iter().enumerate() { + for (i, mut t) in tt.iter().enumerate() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd // implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623 - if let tt::TokenTree::Subtree(tt::Subtree { delimiter: delim, token_trees }) = t { - if let [tt] = &**token_trees { - if delim.kind == tt::DelimiterKind::Parenthesis { - t = tt; + if let TtElement::Subtree(subtree, subtree_iter) = &t { + if let [tt::TokenTree::Leaf(tt)] = subtree_iter.remaining().flat_tokens() { + if subtree.delimiter.kind == tt::DelimiterKind::Parenthesis { + t = TtElement::Leaf(tt); } } } match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { + TtElement::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. @@ -511,28 +495,28 @@ fn concat_expand( } } // handle boolean literals - tt::TokenTree::Leaf(tt::Leaf::Ident(id)) + TtElement::Leaf(tt::Leaf::Ident(id)) if i % 2 == 0 && (id.sym == sym::true_ || id.sym == sym::false_) => { text.push_str(id.sym.as_str()); record_span(id.span); } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { err.get_or_insert(ExpandError::other(call_site, "unexpected token")); } } } - let span = span.unwrap_or(tt.delimiter.open); + let span = span.unwrap_or_else(|| tt.top_subtree().delimiter.open); ExpandResult { value: quote!(span =>#text), err } } fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let mut bytes = String::new(); let mut err = None; let mut span: Option<Span> = None; @@ -541,9 +525,9 @@ fn concat_bytes_expand( Some(_) => (), None => span = Some(s), }; - for (i, t) in tt.token_trees.iter().enumerate() { + for (i, t) in tt.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind, @@ -570,10 +554,12 @@ fn concat_bytes_expand( } } } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), - tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => { + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Subtree(tree, tree_iter) + if tree.delimiter.kind == tt::DelimiterKind::Bracket => + { if let Err(e) = - concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span, call_site) + concat_bytes_expand_subtree(tree_iter, &mut bytes, &mut record_span, call_site) { err.get_or_insert(e); break; @@ -585,31 +571,30 @@ fn concat_bytes_expand( } } } - let span = span.unwrap_or(tt.delimiter.open); + let span = span.unwrap_or(tt.top_subtree().delimiter.open); ExpandResult { - value: tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + value: tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: Symbol::intern(&bytes), span, kind: tt::LitKind::ByteStr, suffix: None, - }))] - .into(), - }, + })], + ), err, } } fn concat_bytes_expand_subtree( - tree: &tt::Subtree, + tree_iter: TtIter<'_>, bytes: &mut String, mut record_span: impl FnMut(Span), err_span: Span, ) -> Result<(), ExpandError> { - for (ti, tt) in tree.token_trees.iter().enumerate() { + for (ti, tt) in tree_iter.enumerate() { match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Byte, @@ -620,7 +605,7 @@ fn concat_bytes_expand_subtree( } record_span(*span); } - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Integer, @@ -631,7 +616,7 @@ fn concat_bytes_expand_subtree( bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); } } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), _ => { return Err(ExpandError::other(err_span, "unexpected token")); } @@ -643,17 +628,17 @@ fn concat_bytes_expand_subtree( fn concat_idents_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let mut err = None; let mut ident = String::new(); - for (i, t) in tt.token_trees.iter().enumerate() { + for (i, t) in tt.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => { + TtElement::Leaf(tt::Leaf::Ident(id)) => { ident.push_str(id.sym.as_str()); } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { err.get_or_insert(ExpandError::other(span, "unexpected token")); } @@ -685,18 +670,19 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { - tt.token_trees - .first() - .ok_or(tt.delimiter.open.cover(tt.delimiter.close)) +fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { + let delimiter = tt.top_subtree().delimiter; + tt.iter() + .next() + .ok_or(delimiter.open.cover(delimiter.close)) .and_then(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Str, suffix: _, })) => Ok((unescape_str(text), *span)), - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::StrRaw(_), @@ -705,26 +691,30 @@ fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { // FIXME: We wrap expression fragments in parentheses which can break this expectation // here // Remove this once we handle none delims correctly - tt::TokenTree::Subtree(tt) if tt.delimiter.kind == DelimiterKind::Parenthesis => { - tt.token_trees.first().and_then(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::Str, - suffix: _, - })) => Some((unescape_str(text), *span)), - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::StrRaw(_), - suffix: _, - })) => Some((text.clone(), *span)), - _ => None, - }) + TtElement::Subtree(tt, mut tt_iter) + if tt.delimiter.kind == DelimiterKind::Parenthesis => + { + tt_iter + .next() + .and_then(|tt| match tt { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::Str, + suffix: _, + })) => Some((unescape_str(text), *span)), + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::StrRaw(_), + suffix: _, + })) => Some((text.clone(), *span)), + _ => None, + }) + .ok_or(delimiter.open.cover(delimiter.close)) } - .ok_or(tt.delimiter.open.cover(tt.delimiter.close)), - ::tt::TokenTree::Leaf(l) => Err(*l.span()), - ::tt::TokenTree::Subtree(tt) => Err(tt.delimiter.open.cover(tt.delimiter.close)), + TtElement::Leaf(l) => Err(*l.span()), + TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)), }) .map_err(|span| ExpandError::other(span, "expected string literal")) } @@ -732,13 +722,16 @@ fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { fn include_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let file_id = match include_input_to_file_id(db, arg_id, tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; let span_map = db.real_span_map(file_id); @@ -754,7 +747,7 @@ fn include_expand( pub fn include_input_to_file_id( db: &dyn ExpandDatabase, arg_id: MacroCallId, - arg: &tt::Subtree, + arg: &tt::TopSubtree, ) -> Result<EditionedFileId, ExpandError> { let (s, span) = parse_string(arg)?; relative_file(db, arg_id, s.as_str(), false, span) @@ -763,32 +756,35 @@ pub fn include_input_to_file_id( fn include_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { // FIXME: actually read the file here if the user asked for macro expansion - let res = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + let res = tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: Symbol::empty(), span, kind: tt::LitKind::ByteStrRaw(1), suffix: None, - }))]), - }; + })], + ); ExpandResult::ok(res) } fn include_str_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let (path, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; @@ -817,13 +813,16 @@ fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> fn env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; @@ -852,14 +851,14 @@ fn env_expand( fn option_env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(DelimSpan { open: call_site, close: call_site }), e, ) } @@ -879,11 +878,11 @@ fn option_env_expand( fn quote_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult<tt::Subtree> { +) -> ExpandResult<tt::TopSubtree> { ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), + tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), ExpandError::other(span, "quote! is not implemented"), ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 418d8d9660b..6c1abc26203 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -6,7 +6,7 @@ use span::Span; use syntax::ToSmolStr; use tt::IdentIsRaw; -use crate::name::Name; +use crate::{name::Name, tt::TopSubtreeBuilder}; pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> { tt::Ident { sym: sym::dollar_crate.clone(), span, is_raw: tt::IdentIsRaw::No } @@ -20,119 +20,93 @@ pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> { #[doc(hidden)] #[macro_export] macro_rules! quote_impl__ { - ($span:ident) => { - Vec::<$crate::tt::TokenTree>::new() - }; + ($span:ident $builder:ident) => {}; - ( @SUBTREE($span:ident) $delim:ident $($tt:tt)* ) => { + ( @SUBTREE($span:ident $builder:ident) $delim:ident $($tt:tt)* ) => { { - let children = $crate::builtin::quote::__quote!($span $($tt)*); - $crate::tt::Subtree { - delimiter: $crate::tt::Delimiter { - kind: $crate::tt::DelimiterKind::$delim, - open: $span, - close: $span, - }, - token_trees: $crate::builtin::quote::IntoTt::to_tokens(children).into_boxed_slice(), - } + $builder.open($crate::tt::DelimiterKind::$delim, $span); + $crate::builtin::quote::__quote!($span $builder $($tt)*); + $builder.close($span); } }; - ( @PUNCT($span:ident) $first:literal ) => { - { - vec![ - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $first, - spacing: $crate::tt::Spacing::Alone, - span: $span, - }).into() - ] - } + ( @PUNCT($span:ident $builder:ident) $first:literal ) => { + $builder.push( + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $first, + spacing: $crate::tt::Spacing::Alone, + span: $span, + }) + ); }; - ( @PUNCT($span:ident) $first:literal, $sec:literal ) => { - { - vec![ - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $first, - spacing: $crate::tt::Spacing::Joint, - span: $span, - }).into(), - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $sec, - spacing: $crate::tt::Spacing::Alone, - span: $span, - }).into() - ] - } + ( @PUNCT($span:ident $builder:ident) $first:literal, $sec:literal ) => { + $builder.extend([ + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $first, + spacing: $crate::tt::Spacing::Joint, + span: $span, + }), + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $sec, + spacing: $crate::tt::Spacing::Alone, + span: $span, + }) + ]); }; // hash variable - ($span:ident # $first:ident $($tail:tt)* ) => { - { - let token = $crate::builtin::quote::ToTokenTree::to_token($first, $span); - let mut tokens = vec![token.into()]; - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - tokens.append(&mut tail_tokens); - tokens - } + ($span:ident $builder:ident # $first:ident $($tail:tt)* ) => { + $crate::builtin::quote::ToTokenTree::to_tokens($first, $span, $builder); + $crate::builtin::quote::__quote!($span $builder $($tail)*); }; - ($span:ident ## $first:ident $($tail:tt)* ) => { - { - let mut tokens = $first.into_iter().map(|it| $crate::builtin::quote::ToTokenTree::to_token(it, $span)).collect::<Vec<crate::tt::TokenTree>>(); - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - tokens.append(&mut tail_tokens); - tokens - } - }; + ($span:ident $builder:ident ## $first:ident $($tail:tt)* ) => {{ + ::std::iter::IntoIterator::into_iter($first).for_each(|it| $crate::builtin::quote::ToTokenTree::to_tokens(it, $span, $builder)); + $crate::builtin::quote::__quote!($span $builder $($tail)*); + }}; // Brace - ($span:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Brace $($tt)*) }; + ($span:ident $builder:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Brace $($tt)*) }; // Bracket - ($span:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Bracket $($tt)*) }; + ($span:ident $builder:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Bracket $($tt)*) }; // Parenthesis - ($span:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Parenthesis $($tt)*) }; + ($span:ident $builder:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Parenthesis $($tt)*) }; // Literal - ($span:ident $tt:literal ) => { vec![$crate::builtin::quote::ToTokenTree::to_token($tt, $span).into()] }; + ($span:ident $builder:ident $tt:literal ) => { $crate::builtin::quote::ToTokenTree::to_tokens($tt, $span, $builder) }; // Ident - ($span:ident $tt:ident ) => { - vec![ { + ($span:ident $builder:ident $tt:ident ) => { + $builder.push( $crate::tt::Leaf::Ident($crate::tt::Ident { sym: intern::Symbol::intern(stringify!($tt)), span: $span, is_raw: tt::IdentIsRaw::No, - }).into() - }] + }) + ); }; // Puncts // FIXME: Not all puncts are handled - ($span:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '-', '>')}; - ($span:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '=', '>')}; - ($span:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '&')}; - ($span:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ',')}; - ($span:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':')}; - ($span:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ';')}; - ($span:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':', ':')}; - ($span:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '.')}; - ($span:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '<')}; - ($span:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '>')}; - ($span:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '!')}; - ($span:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '#')}; - ($span:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '$')}; - ($span:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '*')}; - - ($span:ident $first:tt $($tail:tt)+ ) => { - { - let mut tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $first )); - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - - tokens.append(&mut tail_tokens); - tokens - } - }; + ($span:ident $builder:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '-', '>')}; + ($span:ident $builder:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=', '>')}; + ($span:ident $builder:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '&')}; + ($span:ident $builder:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ',')}; + ($span:ident $builder:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':')}; + ($span:ident $builder:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ';')}; + ($span:ident $builder:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':', ':')}; + ($span:ident $builder:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '.')}; + ($span:ident $builder:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '<')}; + ($span:ident $builder:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '>')}; + ($span:ident $builder:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '!')}; + ($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')}; + ($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')}; + ($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')}; + + ($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{ + $crate::builtin::quote::__quote!($span $builder $first); + $crate::builtin::quote::__quote!($span $builder $($tail)*); + }}; } pub use quote_impl__ as __quote; @@ -141,52 +115,68 @@ pub use quote_impl__ as __quote; #[macro_export] macro_rules! quote { ($span:ident=> $($tt:tt)* ) => { - $crate::builtin::quote::IntoTt::to_subtree($crate::builtin::quote::__quote!($span $($tt)*), $span) + { + let mut builder = $crate::tt::TopSubtreeBuilder::new($crate::tt::Delimiter { + kind: $crate::tt::DelimiterKind::Invisible, + open: $span, + close: $span, + }); + #[allow(unused)] + let builder_ref = &mut builder; + $crate::builtin::quote::__quote!($span builder_ref $($tt)*); + builder.build_skip_top_subtree() + } } } pub(super) use quote; -pub trait IntoTt { - fn to_subtree(self, span: Span) -> crate::tt::Subtree; - fn to_tokens(self) -> Vec<crate::tt::TokenTree>; +pub trait ToTokenTree { + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder); } -impl IntoTt for Vec<crate::tt::TokenTree> { - fn to_subtree(self, span: Span) -> crate::tt::Subtree { - crate::tt::Subtree { - delimiter: crate::tt::Delimiter::invisible_spanned(span), - token_trees: self.into_boxed_slice(), - } - } - - fn to_tokens(self) -> Vec<crate::tt::TokenTree> { - self - } +/// Wraps `TokenTreesView` with a delimiter (a subtree, but without allocating). +pub struct WithDelimiter<'a> { + pub delimiter: crate::tt::Delimiter, + pub token_trees: crate::tt::TokenTreesView<'a>, } -impl IntoTt for crate::tt::Subtree { - fn to_subtree(self, _: Span) -> crate::tt::Subtree { - self +impl ToTokenTree for WithDelimiter<'_> { + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) { + builder.open(self.delimiter.kind, self.delimiter.open); + self.token_trees.to_tokens(span, builder); + builder.close(self.delimiter.close); } +} - fn to_tokens(self) -> Vec<crate::tt::TokenTree> { - vec![crate::tt::TokenTree::Subtree(self)] +impl ToTokenTree for crate::tt::TokenTreesView<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_with_tt(self); } } -pub trait ToTokenTree { - fn to_token(self, span: Span) -> crate::tt::TokenTree; +impl ToTokenTree for crate::tt::SubtreeView<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_with_tt(self.as_token_trees()); + } } -impl ToTokenTree for crate::tt::TokenTree { - fn to_token(self, _: Span) -> crate::tt::TokenTree { - self +impl ToTokenTree for crate::tt::TopSubtree { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_tt_dangerous(self.0); } } -impl ToTokenTree for crate::tt::Subtree { - fn to_token(self, _: Span) -> crate::tt::TokenTree { - self.into() +impl ToTokenTree for crate::tt::TtElement<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + match self { + crate::tt::TtElement::Leaf(leaf) => builder.push(leaf.clone()), + crate::tt::TtElement::Subtree(subtree, subtree_iter) => { + builder.extend_tt_dangerous( + std::iter::once(crate::tt::TokenTree::Subtree(subtree.clone())) + .chain(subtree_iter.remaining().flat_tokens().iter().cloned()), + ); + } + } } } @@ -194,18 +184,17 @@ macro_rules! impl_to_to_tokentrees { ($($span:ident: $ty:ty => $this:ident $im:block;)*) => { $( impl ToTokenTree for $ty { - fn to_token($this, $span: Span) -> crate::tt::TokenTree { + fn to_tokens($this, $span: Span, builder: &mut TopSubtreeBuilder) { let leaf: crate::tt::Leaf = $im.into(); - leaf.into() + builder.push(leaf); } } )* } } - impl<T: ToTokenTree + Clone> ToTokenTree for &T { - fn to_token(self, span: Span) -> crate::tt::TokenTree { - self.clone().to_token(span) + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) { + self.clone().to_tokens(span, builder); } } @@ -316,18 +305,15 @@ mod tests { // } let struct_name = mk_ident("Foo"); let fields = [mk_ident("name"), mk_ident("id")]; - let fields = fields - .iter() - .flat_map(|it| quote!(DUMMY =>#it: self.#it.clone(), ).token_trees.into_vec()); - - let list = crate::tt::Subtree { - delimiter: crate::tt::Delimiter { - kind: crate::tt::DelimiterKind::Brace, - open: DUMMY, - close: DUMMY, - }, - token_trees: fields.collect(), - }; + let fields = fields.iter().map(|it| quote!(DUMMY =>#it: self.#it.clone(), )); + + let mut builder = tt::TopSubtreeBuilder::new(crate::tt::Delimiter { + kind: crate::tt::DelimiterKind::Brace, + open: DUMMY, + close: DUMMY, + }); + fields.for_each(|field| builder.extend_with_tt(field.view().as_token_trees())); + let list = builder.build(); let quoted = quote! {DUMMY => impl Clone for #struct_name { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index fa400378f3a..f4e80ef9e26 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -28,7 +28,7 @@ use crate::{ MacroDefId, MacroDefKind, MacroFileId, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same -type MacroArgResult = (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span); +type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span); /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and @@ -123,7 +123,7 @@ pub trait ExpandDatabase: SourceDatabase { /// proc macros, since they are not deterministic in general, and /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details - fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>; + fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::TopSubtree>>; /// Retrieves the span to be used for a proc-macro expansions spans. /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to /// directly depend on as that would cause to frequent invalidations, mainly because of the @@ -251,7 +251,7 @@ pub fn expand_speculative( span, DocCommentDesugarMode::ProcMacro, ); - tree.delimiter = tt::Delimiter::invisible_spanned(span); + *tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span); Some(tree) } @@ -266,7 +266,7 @@ pub fn expand_speculative( let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(ast, expander, _) => { let span = db.proc_macro_span(ast); - tt.delimiter = tt::Delimiter::invisible_spanned(span); + *tt.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span); expander.expand( db, loc.def.krate, @@ -429,10 +429,10 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let dummy_tt = |kind| { ( - Arc::new(tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind }, - token_trees: Box::default(), - }), + Arc::new(tt::TopSubtree::from_token_trees( + tt::Delimiter { open: span, close: span, kind }, + tt::TokenTreesView::new(&[]), + )), SyntaxFixupUndoInfo::default(), span, ) @@ -479,7 +479,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { ); if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; } return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); } @@ -537,7 +537,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; } (Arc::new(tt), undo_info, span) @@ -592,7 +592,7 @@ fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc<tt::Subtree>, MatchedArmIndex)> { +) -> ExpandResult<(CowArc<tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { @@ -655,12 +655,7 @@ fn macro_expand( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value - .map(|()| { - CowArc::Owned(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([]), - }) - }) + .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) .zip_val(matched_arm); } } @@ -679,7 +674,10 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span { span_map.span_for_range(range) } -fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> { +fn expand_proc_macro( + db: &dyn ExpandDatabase, + id: MacroCallId, +) -> ExpandResult<Arc<tt::TopSubtree>> { let loc = db.lookup_intern_macro_call(id); let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); @@ -709,12 +707,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { - return value.map(|()| { - Arc::new(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([]), - }) - }); + return value.map(|()| Arc::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))); } fixup::reverse_fixups(&mut tt, &undo_info); @@ -723,7 +716,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A } fn token_tree_to_syntax_node( - tt: &tt::Subtree, + tt: &tt::TopSubtree, expand_to: ExpandTo, edition: parser::Edition, ) -> (Parse<SyntaxNode>, ExpansionSpanMap) { @@ -737,7 +730,8 @@ fn token_tree_to_syntax_node( syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition) } -fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { +fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { + let tt = tt.top_subtree(); let count = tt.count(); if TOKEN_LIMIT.check(count).is_err() { Err(ExpandResult { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 86dd4aef090..d1c39f32ca3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -26,14 +26,14 @@ impl DeclarativeMacroExpander { pub fn expand( &self, db: &dyn ExpandDatabase, - tt: tt::Subtree, + tt: tt::TopSubtree, call_id: MacroCallId, span: Span, - ) -> ExpandResult<(tt::Subtree, Option<u32>)> { + ) -> ExpandResult<(tt::TopSubtree, Option<u32>)> { let loc = db.lookup_intern_macro_call(call_id); match self.mac.err() { Some(_) => ExpandResult::new( - (tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None), + (tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None), ExpandError::new(span, ExpandErrorKind::MacroDefinition), ), None => self @@ -50,13 +50,13 @@ impl DeclarativeMacroExpander { pub fn expand_unhygienic( &self, - tt: tt::Subtree, + tt: tt::TopSubtree, call_site: Span, def_site_edition: Edition, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { match self.mac.err() { Some(_) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MacroDefinition), ), None => self @@ -78,7 +78,7 @@ impl DeclarativeMacroExpander { let transparency = |node| { // ... would be nice to have the item tree here let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate); - match &*attrs + match attrs .iter() .find(|it| { it.path @@ -87,7 +87,8 @@ impl DeclarativeMacroExpander { .unwrap_or(false) })? .token_tree_value()? - .token_trees + .token_trees() + .flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym { s if *s == sym::transparent => Some(Transparency::Transparent), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 1dadfe2ba99..f476d1b564c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -89,7 +89,7 @@ pub fn expand_eager_macro_input( DocCommentDesugarMode::Mbe, ); - subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; + subtree.top_subtree_delimiter_mut().kind = crate::tt::DelimiterKind::Invisible; let loc = MacroCallLoc { def, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 0af29681a13..3d2d52a0afe 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -3,7 +3,6 @@ use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::SmallVec; use span::{ ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, FIXUP_ERASED_FILE_AST_ID_MARKER, ROOT_ERASED_FILE_AST_ID, @@ -19,7 +18,7 @@ use tt::Spacing; use crate::{ span_map::SpanMapRef, - tt::{Ident, Leaf, Punct, Subtree}, + tt::{self, Ident, Leaf, Punct, TopSubtree}, }; /// The result of calculating fixes for a syntax node -- a bunch of changes @@ -36,7 +35,7 @@ pub(crate) struct SyntaxFixups { #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { // FIXME: ThinArc<[Subtree]> - original: Option<Arc<Box<[Subtree]>>>, + original: Option<Arc<Box<[TopSubtree]>>>, } impl SyntaxFixupUndoInfo { @@ -110,7 +109,7 @@ pub(crate) fn fixup_syntax( } }, ast::ExprStmt(it) => { - let needs_semi = it.semicolon_token().is_none() && it.expr().map_or(false, |e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR); + let needs_semi = it.semicolon_token().is_none() && it.expr().is_some_and(|e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR); if needs_semi { append.insert(node.clone().into(), vec![ Leaf::Punct(Punct { @@ -369,68 +368,126 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c)) } -pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) { +pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) { let Some(undo_info) = undo_info.original.as_deref() else { return }; let undo_info = &**undo_info; + let delimiter = tt.top_subtree_delimiter_mut(); #[allow(deprecated)] if never!( - tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID - || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID + delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID ) { let span = |file_id| Span { range: TextRange::empty(TextSize::new(0)), anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, ctx: SyntaxContextId::ROOT, }; - tt.delimiter.open = span(tt.delimiter.open.anchor.file_id); - tt.delimiter.close = span(tt.delimiter.close.anchor.file_id); + delimiter.open = span(delimiter.open.anchor.file_id); + delimiter.close = span(delimiter.close.anchor.file_id); } reverse_fixups_(tt, undo_info); } -fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { - let tts = std::mem::take(&mut tt.token_trees).into_vec(); - tt.token_trees = tts - .into_iter() - // delete all fake nodes - .filter(|tt| match tt { - tt::TokenTree::Leaf(leaf) => { - let span = leaf.span(); - let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID; - let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END; - is_real_leaf || is_replaced_node +#[derive(Debug)] +enum TransformTtAction<'a> { + Keep, + ReplaceWith(tt::TokenTreesView<'a>), +} + +impl TransformTtAction<'_> { + fn remove() -> Self { + Self::ReplaceWith(tt::TokenTreesView::new(&[])) + } +} + +/// This function takes a token tree, and calls `callback` with each token tree in it. +/// Then it does what the callback says: keeps the tt or replaces it with a (possibly empty) +/// tts view. +fn transform_tt<'a, 'b>( + tt: &'a mut Vec<tt::TokenTree>, + mut callback: impl FnMut(&mut tt::TokenTree) -> TransformTtAction<'b>, +) { + // We need to keep a stack of the currently open subtrees, because we need to update + // them if we change the number of items in them. + let mut subtrees_stack = Vec::new(); + let mut i = 0; + while i < tt.len() { + 'pop_finished_subtrees: while let Some(&subtree_idx) = subtrees_stack.last() { + let tt::TokenTree::Subtree(subtree) = &tt[subtree_idx] else { + unreachable!("non-subtree on subtrees stack"); + }; + if i >= subtree_idx + 1 + subtree.usize_len() { + subtrees_stack.pop(); + } else { + break 'pop_finished_subtrees; } - tt::TokenTree::Subtree(_) => true, - }) - .flat_map(|tt| match tt { - tt::TokenTree::Subtree(mut tt) => { - if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID - || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID - { - // Even though fixup never creates subtrees with fixup spans, the old proc-macro server - // might copy them if the proc-macro asks for it, so we need to filter those out - // here as well. - return SmallVec::new_const(); + } + + let action = callback(&mut tt[i]); + match action { + TransformTtAction::Keep => { + // This cannot be shared with the replaced case, because then we may push the same subtree + // twice, and will update it twice which will lead to errors. + if let tt::TokenTree::Subtree(_) = &tt[i] { + subtrees_stack.push(i); } - reverse_fixups_(&mut tt, undo_info); - SmallVec::from_const([tt.into()]) + + i += 1; } - tt::TokenTree::Leaf(leaf) => { - if leaf.span().anchor.ast_id == FIXUP_DUMMY_AST_ID { - // we have a fake node here, we need to replace it again with the original - let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); - if original.delimiter.kind == tt::DelimiterKind::Invisible { - SmallVec::from(original.token_trees.into_vec()) - } else { - SmallVec::from_const([original.into()]) - } - } else { - // just a normal leaf - SmallVec::from_const([leaf.into()]) + TransformTtAction::ReplaceWith(replacement) => { + let old_len = 1 + match &tt[i] { + tt::TokenTree::Leaf(_) => 0, + tt::TokenTree::Subtree(subtree) => subtree.usize_len(), + }; + let len_diff = replacement.len() as i64 - old_len as i64; + tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned()); + // `+1` for the loop. + i = i.checked_add_signed(len_diff as isize + 1).unwrap(); + + for &subtree_idx in &subtrees_stack { + let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else { + unreachable!("non-subtree on subtrees stack"); + }; + subtree.len = (i64::from(subtree.len) + len_diff).try_into().unwrap(); } } - }) - .collect(); + } + } +} + +fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) { + let mut tts = std::mem::take(&mut tt.0).into_vec(); + transform_tt(&mut tts, |tt| match tt { + tt::TokenTree::Leaf(leaf) => { + let span = leaf.span(); + let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID; + let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END; + if !is_real_leaf && !is_replaced_node { + return TransformTtAction::remove(); + } + + if !is_real_leaf { + // we have a fake node here, we need to replace it again with the original + let original = &undo_info[u32::from(leaf.span().range.start()) as usize]; + TransformTtAction::ReplaceWith(original.view().strip_invisible()) + } else { + // just a normal leaf + TransformTtAction::Keep + } + } + tt::TokenTree::Subtree(tt) => { + if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID + { + // Even though fixup never creates subtrees with fixup spans, the old proc-macro server + // might copy them if the proc-macro asks for it, so we need to filter those out + // here as well. + return TransformTtAction::remove(); + } + TransformTtAction::Keep + } + }); + tt.0 = tts.into_boxed_slice(); } #[cfg(test)] @@ -458,16 +515,18 @@ mod tests { } } - fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool { - a.delimiter.kind == b.delimiter.kind - && a.token_trees.len() == b.token_trees.len() - && a.token_trees.iter().zip(b.token_trees.iter()).all(|(a, b)| check_tt_eq(a, b)) + fn check_subtree_eq(a: &tt::TopSubtree, b: &tt::TopSubtree) -> bool { + let a = a.view().as_token_trees().flat_tokens(); + let b = b.view().as_token_trees().flat_tokens(); + a.len() == b.len() && std::iter::zip(a, b).all(|(a, b)| check_tt_eq(a, b)) } fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool { match (a, b) { (tt::TokenTree::Leaf(a), tt::TokenTree::Leaf(b)) => check_leaf_eq(a, b), - (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => check_subtree_eq(a, b), + (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => { + a.delimiter.kind == b.delimiter.kind + } _ => false, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 5aafe6db527..a0c4c125db4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -70,12 +70,17 @@ pub mod tt { pub type Delimiter = ::tt::Delimiter<Span>; pub type DelimSpan = ::tt::DelimSpan<Span>; pub type Subtree = ::tt::Subtree<Span>; - pub type SubtreeBuilder = ::tt::SubtreeBuilder<Span>; pub type Leaf = ::tt::Leaf<Span>; pub type Literal = ::tt::Literal<Span>; pub type Punct = ::tt::Punct<Span>; pub type Ident = ::tt::Ident<Span>; pub type TokenTree = ::tt::TokenTree<Span>; + pub type TopSubtree = ::tt::TopSubtree<Span>; + pub type TopSubtreeBuilder = ::tt::TopSubtreeBuilder<Span>; + pub type TokenTreesView<'a> = ::tt::TokenTreesView<'a, Span>; + pub type SubtreeView<'a> = ::tt::SubtreeView<'a, Span>; + pub type TtElement<'a> = ::tt::iter::TtElement<'a, Span>; + pub type TtIter<'a> = ::tt::iter::TtIter<'a, Span>; } #[macro_export] @@ -284,7 +289,7 @@ impl MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc<tt::Subtree>, + arg: Arc<tt::TopSubtree>, /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option<ExpandError>, @@ -320,7 +325,7 @@ pub enum MacroCallKind { ast_id: AstId<ast::Item>, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index` // but we need to fix the `cfg_attr` handling first. - attr_args: Option<Arc<tt::Subtree>>, + attr_args: Option<Arc<tt::TopSubtree>>, /// Syntactical index of the invoking `#[attribute]`. /// /// Outer attributes are counted first, then inner attributes. This does not support @@ -1044,7 +1049,7 @@ impl ExpandTo { if parent.kind() == MACRO_EXPR && parent .parent() - .map_or(false, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS)) + .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS)) { return ExpandTo::Statements; } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index dcf2af39979..7ecf5219873 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -58,7 +58,7 @@ impl ModPath { convert_path(db, path, span_for_range) } - pub fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> { + pub fn from_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> { convert_path_tt(db, tt) } @@ -315,10 +315,10 @@ fn convert_path( Some(mod_path) } -fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> { +fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> { let mut leaves = tt.iter().filter_map(|tt| match tt { - tt::TokenTree::Leaf(leaf) => Some(leaf), - tt::TokenTree::Subtree(_) => None, + tt::TtElement::Leaf(leaf) => Some(leaf), + tt::TtElement::Subtree(..) => None, }); let mut segments = smallvec::smallvec![]; let kind = match leaves.next()? { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index fe09f0307c9..07808fea85b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -23,14 +23,14 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, + subtree: &tt::TopSubtree, + attrs: Option<&tt::TopSubtree>, env: &Env, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option<String>, - ) -> Result<tt::Subtree, ProcMacroExpansionError>; + ) -> Result<tt::TopSubtree, ProcMacroExpansionError>; } #[derive(Debug)] @@ -201,23 +201,23 @@ impl CustomProcMacroExpander { db: &dyn ExpandDatabase, def_crate: CrateId, calling_crate: CrateId, - tt: &tt::Subtree, - attr_arg: Option<&tt::Subtree>, + tt: &tt::TopSubtree, + attr_arg: Option<&tt::TopSubtree>, def_site: Span, call_site: Span, mixed_site: Span, - ) -> ExpandResult<tt::Subtree> { + ) -> ExpandResult<tt::TopSubtree> { match self.proc_macro_id { Self::PROC_MACRO_ATTR_DISABLED => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::ProcMacroAttrExpansionDisabled), ), Self::MISSING_EXPANDER => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MissingProcMacroExpander(def_crate)), ), Self::DISABLED_ID => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MacroDisabled), ), id => { @@ -226,7 +226,10 @@ impl CustomProcMacroExpander { Ok(proc_macro) => proc_macro, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { + open: call_site, + close: call_site, + }), e, ) } @@ -260,7 +263,10 @@ impl CustomProcMacroExpander { } ProcMacroExpansionError::System(text) | ProcMacroExpansionError::Panic(text) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { + open: call_site, + close: call_site, + }), ExpandError::new( call_site, ExpandErrorKind::ProcMacroPanic(text.into_boxed_str()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 55d0edd5e0c..9f01f1eb259 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -13,7 +13,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ - data::adt::StructFlags, + data::{adt::StructFlags, TraitFlags}, hir::Movability, lang_item::{LangItem, LangItemTarget}, AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, @@ -675,13 +675,13 @@ pub(crate) fn trait_datum_query( let generic_params = generics(db.upcast(), trait_.into()); let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); let flags = rust_ir::TraitFlags { - auto: trait_data.is_auto, + auto: trait_data.flags.contains(TraitFlags::IS_AUTO), upstream: trait_.lookup(db.upcast()).container.krate() != krate, non_enumerable: true, coinductive: false, // only relevant for Chalk testing // FIXME: set these flags correctly marker: false, - fundamental: trait_data.fundamental, + fundamental: trait_data.flags.contains(TraitFlags::IS_FUNDAMENTAL), }; let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); @@ -950,11 +950,18 @@ pub(crate) fn fn_def_datum_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Ar pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances { let callable_def: CallableDefId = from_chalk(db, fn_def_id); - let generic_params = - generics(db.upcast(), GenericDefId::from_callable(db.upcast(), callable_def)); Variances::from_iter( Interner, - std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), + db.variances_of(GenericDefId::from_callable(db.upcast(), callable_def)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| match v { + crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, + crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, + crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, + crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, + }), ) } @@ -962,10 +969,14 @@ pub(crate) fn adt_variance_query( db: &dyn HirDatabase, chalk_ir::AdtId(adt_id): AdtId, ) -> Variances { - let generic_params = generics(db.upcast(), adt_id.into()); Variances::from_iter( Interner, - std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), + db.variances_of(adt_id.into()).as_deref().unwrap_or_default().iter().map(|v| match v { + crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, + crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, + crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, + crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, + }), ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 302558162ac..51c178b90d7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -443,13 +443,25 @@ impl ProjectionTyExt for ProjectionTy { } pub trait DynTyExt { - fn principal(&self) -> Option<&TraitRef>; + fn principal(&self) -> Option<Binders<Binders<&TraitRef>>>; + fn principal_id(&self) -> Option<chalk_ir::TraitId<Interner>>; } impl DynTyExt for DynTy { - fn principal(&self) -> Option<&TraitRef> { + fn principal(&self) -> Option<Binders<Binders<&TraitRef>>> { + self.bounds.as_ref().filter_map(|bounds| { + bounds.interned().first().and_then(|b| { + b.as_ref().filter_map(|b| match b { + crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), + _ => None, + }) + }) + }) + } + + fn principal_id(&self) -> Option<chalk_ir::TraitId<Interner>> { self.bounds.skip_binders().interned().first().and_then(|b| match b.skip_binders() { - crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), + crate::WhereClause::Implemented(trait_ref) => Some(trait_ref.trait_id), _ => None, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 6856eaa3e02..6b056826670 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -271,6 +271,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[ra_salsa::invoke(chalk_db::adt_variance_query)] fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances; + #[ra_salsa::invoke(crate::variance::variances_of)] + #[ra_salsa::cycle(crate::variance::variances_of_cycle)] + fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>; + #[ra_salsa::invoke(chalk_db::associated_ty_value_query)] fn associated_ty_value( &self, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs index aa0c9e30be1..348f8a0f4a8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs @@ -49,7 +49,7 @@ fn is_camel_case(name: &str) -> bool { let mut fst = None; // start with a non-lowercase letter rather than non-uppercase // ones (some scripts don't have a concept of upper/lowercase) - name.chars().next().map_or(true, |c| !c.is_lowercase()) + name.chars().next().is_none_or(|c| !c.is_lowercase()) && !name.contains("__") && !name.chars().any(|snd| { let ret = match fst { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 92404e3a10e..7f9f0c0de19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -295,7 +295,7 @@ impl ExprValidator { path, self.body.expr_path_hygiene(scrutinee_expr), ); - value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_))) + value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { TyKind::Adt(adt, ..) @@ -447,7 +447,7 @@ impl ExprValidator { loop { let parent = top_if_expr.syntax().parent(); let has_parent_expr_stmt_or_stmt_list = - parent.as_ref().map_or(false, |node| { + parent.as_ref().is_some_and(|node| { ast::ExprStmt::can_cast(node.kind()) | ast::StmtList::can_cast(node.kind()) }); @@ -529,7 +529,7 @@ impl FilterMapNextChecker { let is_dyn_trait = self .prev_receiver_ty .as_ref() - .map_or(false, |it| it.strip_references().dyn_trait().is_some()); + .is_some_and(|it| it.strip_references().dyn_trait().is_some()); if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait { return Some(()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 5452f5c680d..2b854310a15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -314,7 +314,7 @@ impl<'db> MatchCheckCtx<'db> { } } -impl<'db> PatCx for MatchCheckCtx<'db> { +impl PatCx for MatchCheckCtx<'_> { type Error = (); type Ty = Ty; type VariantIdx = EnumVariantContiguousIndex; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index de8ce56df64..a4e052a0362 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -474,7 +474,9 @@ impl HirDisplay for ProjectionTy { let trait_ref = self.trait_ref(f.db); write!(f, "<")?; - fmt_trait_ref(f, &trait_ref, true)?; + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; write!( f, ">::{}", @@ -1775,32 +1777,14 @@ fn write_bounds_like_dyn_trait( Ok(()) } -fn fmt_trait_ref( - f: &mut HirFormatter<'_>, - tr: &TraitRef, - use_as: bool, -) -> Result<(), HirDisplayError> { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - - tr.self_type_parameter(Interner).hir_fmt(f)?; - if use_as { - write!(f, " as ")?; - } else { - write!(f, ": ")?; - } - let trait_ = tr.hir_trait_id(); - f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?; - f.end_location_link(); - let substs = tr.substitution.as_slice(Interner); - hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner)) -} - impl HirDisplay for TraitRef { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - fmt_trait_ref(f, self, false) + let trait_ = self.hir_trait_id(); + f.start_location_link(trait_.into()); + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?; + f.end_location_link(); + let substs = self.substitution.as_slice(Interner); + hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner)) } } @@ -1811,10 +1795,17 @@ impl HirDisplay for WhereClause { } match self { - WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, + WhereClause::Implemented(trait_ref) => { + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, ": ")?; + trait_ref.hir_fmt(f)?; + } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { write!(f, "<")?; - fmt_trait_ref(f, &projection_ty.trait_ref(f.db), true)?; + let trait_ref = &projection_ty.trait_ref(f.db); + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; write!(f, ">::",)?; let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); f.start_location_link(type_alias.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index fadf8aca998..6a01579bccc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -9,8 +9,8 @@ use chalk_ir::{ }; use chalk_solve::rust_ir::InlineBound; use hir_def::{ - lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, - TypeAliasId, + data::TraitFlags, lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, + HasModule, TraitId, TypeAliasId, }; use rustc_hash::FxHashSet; use smallvec::SmallVec; @@ -432,7 +432,7 @@ where // Allow `impl AutoTrait` predicates if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); - if trait_data.is_auto + if trait_data.flags.contains(TraitFlags::IS_AUTO) && substitution .as_slice(Interner) .first() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index fe7541d2374..abbf2a4f2ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -26,14 +26,14 @@ use triomphe::Arc; use crate::{db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx, Interner, Substitution}; -pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { +pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); let params = db.generic_params(def); let has_trait_self_param = params.trait_self_param().is_some(); Generics { def, params, parent_generics, has_trait_self_param } } #[derive(Clone, Debug)] -pub(crate) struct Generics { +pub struct Generics { def: GenericDefId, params: Arc<GenericParams>, parent_generics: Option<Box<Generics>>, @@ -153,7 +153,7 @@ impl Generics { (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) } - pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> { + pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> { self.find_type_or_const_param(param) } @@ -174,7 +174,7 @@ impl Generics { } } - pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> { + pub fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> { self.find_lifetime(lifetime) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index dbee5a1a919..25bb3a76de2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -16,6 +16,7 @@ pub(crate) mod cast; pub(crate) mod closure; mod coerce; +mod diagnostics; mod expr; mod mutability; mod pat; @@ -57,15 +58,20 @@ use crate::{ db::HirDatabase, fold_tys, generics::Generics, - infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable}, - lower::{ImplTraitLoweringMode, TyLoweringDiagnostic}, + infer::{ + coerce::CoerceMany, + diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, + expr::ExprIsRead, + unify::InferenceTable, + }, + lower::{diagnostics::TyLoweringDiagnostic, ImplTraitLoweringMode}, mir::MirSpan, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, + PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -276,6 +282,10 @@ pub enum InferenceDiagnostic { source: InferenceTyDiagnosticSource, diag: TyLoweringDiagnostic, }, + PathDiagnostic { + node: ExprOrPatId, + diag: PathLoweringDiagnostic, + }, } /// A mismatch between an expected and an inferred type. @@ -442,6 +452,7 @@ pub struct InferenceResult { /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of /// that which allows us to resolve a [`TupleFieldId`]s type. pub tuple_field_access_types: FxHashMap<TupleId, Substitution>, + /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. pub diagnostics: Vec<InferenceDiagnostic>, pub type_of_expr: ArenaMap<ExprId, Ty>, /// For each pattern record the type it resolves to. @@ -579,6 +590,8 @@ pub(crate) struct InferenceContext<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) owner: DefWithBodyId, pub(crate) body: &'a Body, + /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext + /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver, generics: OnceCell<Option<Generics>>, table: unify::InferenceTable<'a>, @@ -620,6 +633,8 @@ pub(crate) struct InferenceContext<'a> { /// comment on `InferenceContext::sort_closures` closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>, deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>, + + diagnostics: Diagnostics, } #[derive(Clone, Debug)] @@ -701,6 +716,7 @@ impl<'a> InferenceContext<'a> { deferred_closures: FxHashMap::default(), closure_dependencies: FxHashMap::default(), inside_assignment: false, + diagnostics: Diagnostics::default(), } } @@ -724,8 +740,10 @@ impl<'a> InferenceContext<'a> { mut result, mut deferred_cast_checks, tuple_field_accesses_rev, + diagnostics, .. } = self; + let mut diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we // don't forget to handle them here. let InferenceResult { @@ -733,7 +751,6 @@ impl<'a> InferenceContext<'a> { field_resolutions: _, variant_resolutions: _, assoc_resolutions, - diagnostics, type_of_expr, type_of_pat, type_of_binding, @@ -752,6 +769,7 @@ impl<'a> InferenceContext<'a> { mutated_bindings_in_closure: _, tuple_field_access_types: _, coercion_casts, + diagnostics: _, } = &mut result; table.fallback_if_possible(); @@ -866,6 +884,9 @@ impl<'a> InferenceContext<'a> { *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); }) .collect(); + + result.diagnostics = diagnostics; + result } @@ -1238,41 +1259,28 @@ impl<'a> InferenceContext<'a> { self.result.type_of_binding.insert(id, ty); } - fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) { - self.result.diagnostics.push(diagnostic); - } - - fn push_ty_diagnostics( - &mut self, - source: InferenceTyDiagnosticSource, - diagnostics: Vec<TyLoweringDiagnostic>, - ) { - self.result.diagnostics.extend( - diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), - ); + fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) { + self.diagnostics.push(diagnostic); } fn with_ty_lowering<R>( &mut self, types_map: &TypesMap, types_source: InferenceTyDiagnosticSource, - f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R, + f: impl FnOnce(&mut TyLoweringContext<'_>) -> R, ) -> R { - let mut ctx = crate::lower::TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( self.db, &self.resolver, types_map, self.owner.into(), + &self.diagnostics, + types_source, ); - let result = f(&mut ctx); - self.push_ty_diagnostics(types_source, ctx.diagnostics); - result + f(&mut ctx) } - fn with_body_ty_lowering<R>( - &mut self, - f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R, - ) -> R { + fn with_body_ty_lowering<R>(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R { self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f) } @@ -1451,51 +1459,55 @@ impl<'a> InferenceContext<'a> { } } - fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option<VariantId>) { + fn resolve_variant( + &mut self, + node: ExprOrPatId, + path: Option<&Path>, + value_ns: bool, + ) -> (Ty, Option<VariantId>) { let path = match path { Some(path) => path, None => return (self.err_ty(), None), }; - let mut ctx = crate::lower::TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( self.db, &self.resolver, &self.body.types, self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, ); let (resolution, unresolved) = if value_ns { - match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) { - Some(ResolveValueResult::ValueNs(value, _)) => match value { + let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else { + return (self.err_ty(), None); + }; + match res { + ResolveValueResult::ValueNs(value, _) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - self.push_ty_diagnostics( - InferenceTyDiagnosticSource::Body, - ctx.diagnostics, - ); + drop(ctx); let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { let substs = ctx.substs_from_path(path, strukt.into(), true); - self.push_ty_diagnostics( - InferenceTyDiagnosticSource::Body, - ctx.diagnostics, - ); + drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(strukt.into())); } ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), - _ => return (self.err_ty(), None), + _ => { + drop(ctx); + return (self.err_ty(), None); + } }, - Some(ResolveValueResult::Partial(typens, unresolved, _)) => { - (typens, Some(unresolved)) - } - None => return (self.err_ty(), None), + ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)), } } else { - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, idx, _)) => (it, idx), + match ctx.resolve_path_in_type_ns(path, node) { + Some((it, idx)) => (it, idx), None => return (self.err_ty(), None), } }; @@ -1506,21 +1518,21 @@ impl<'a> InferenceContext<'a> { return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = ctx.substs_from_path(path, strukt.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { let substs = ctx.substs_from_path(path, u.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(u.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(var.into())), unresolved) @@ -1531,6 +1543,7 @@ impl<'a> InferenceContext<'a> { let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); let Some(mut remaining_idx) = unresolved else { + drop(ctx); return self.resolve_variant_on_alias(ty, None, mod_path); }; @@ -1538,6 +1551,7 @@ impl<'a> InferenceContext<'a> { // We need to try resolving unresolved segments one by one because each may resolve // to a projection, which `TyLoweringContext` cannot handle on its own. + let mut tried_resolving_once = false; while !remaining_segments.is_empty() { let resolved_segment = path.segments().get(remaining_idx - 1).unwrap(); let current_segment = remaining_segments.take(1); @@ -1558,18 +1572,27 @@ impl<'a> InferenceContext<'a> { } } + if tried_resolving_once { + // FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()` + // will need to be updated to err at the correct segment. + // + // We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()` + // will be incorrect, and that can mess up error reporting. + break; + } + // `lower_partly_resolved_path()` returns `None` as type namespace unless // `remaining_segments` is empty, which is never the case here. We don't know // which namespace the new `ty` is in until normalized anyway. (ty, _) = ctx.lower_partly_resolved_path( + node, resolution, resolved_segment, current_segment, + (remaining_idx - 1) as u32, false, - &mut |_, _reason| { - // FIXME: Report an error. - }, ); + tried_resolving_once = true; ty = self.table.insert_type_vars(ty); ty = self.table.normalize_associated_types_in(ty); @@ -1582,7 +1605,7 @@ impl<'a> InferenceContext<'a> { remaining_idx += 1; remaining_segments = remaining_segments.skip(1); } - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let variant = ty.as_adt().and_then(|(id, _)| match id { AdtId::StructId(s) => Some(VariantId::StructId(s)), @@ -1601,7 +1624,7 @@ impl<'a> InferenceContext<'a> { }; let substs = ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(it.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 5a251683b96..2523aba5383 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -96,8 +96,8 @@ impl InferenceContext<'_> { .map(|b| b.into_value_and_skipped_binders().0); self.deduce_closure_kind_from_predicate_clauses(clauses) } - TyKind::Dyn(dyn_ty) => dyn_ty.principal().and_then(|trait_ref| { - self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_ref.trait_id)) + TyKind::Dyn(dyn_ty) => dyn_ty.principal_id().and_then(|trait_id| { + self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_id)) }), TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => { let clauses = self.clauses_for_self_ty(*ty); @@ -992,7 +992,7 @@ impl InferenceContext<'_> { }, }, } - if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) { + if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; } self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs new file mode 100644 index 00000000000..032dc37899d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -0,0 +1,128 @@ +//! This file contains the [`Diagnostics`] type used during inference, +//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces +//! it and takes care of diagnostics in inference. + +use std::cell::RefCell; +use std::ops::{Deref, DerefMut}; + +use hir_def::body::HygieneId; +use hir_def::hir::ExprOrPatId; +use hir_def::path::{Path, PathSegment, PathSegments}; +use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs}; +use hir_def::type_ref::TypesMap; +use hir_def::TypeOwnerId; + +use crate::db::HirDatabase; +use crate::{ + InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic, +}; + +// Unfortunately, this struct needs to use interior mutability (but we encapsulate it) +// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference +// to our resolver and so we cannot have mutable reference, but we really want to have +// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess. +#[derive(Debug, Default, Clone)] +pub(super) struct Diagnostics(RefCell<Vec<InferenceDiagnostic>>); + +impl Diagnostics { + pub(super) fn push(&self, diagnostic: InferenceDiagnostic) { + self.0.borrow_mut().push(diagnostic); + } + + fn push_ty_diagnostics( + &self, + source: InferenceTyDiagnosticSource, + diagnostics: Vec<TyLoweringDiagnostic>, + ) { + self.0.borrow_mut().extend( + diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), + ); + } + + pub(super) fn finish(self) -> Vec<InferenceDiagnostic> { + self.0.into_inner() + } +} + +pub(super) struct InferenceTyLoweringContext<'a> { + ctx: TyLoweringContext<'a>, + diagnostics: &'a Diagnostics, + source: InferenceTyDiagnosticSource, +} + +impl<'a> InferenceTyLoweringContext<'a> { + pub(super) fn new( + db: &'a dyn HirDatabase, + resolver: &'a Resolver, + types_map: &'a TypesMap, + owner: TypeOwnerId, + diagnostics: &'a Diagnostics, + source: InferenceTyDiagnosticSource, + ) -> Self { + Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source } + } + + pub(super) fn resolve_path_in_type_ns( + &mut self, + path: &Path, + node: ExprOrPatId, + ) -> Option<(TypeNs, Option<usize>)> { + let diagnostics = self.diagnostics; + self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| { + diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }) + }) + } + + pub(super) fn resolve_path_in_value_ns( + &mut self, + path: &Path, + node: ExprOrPatId, + hygiene_id: HygieneId, + ) -> Option<ResolveValueResult> { + let diagnostics = self.diagnostics; + self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| { + diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }) + }) + } + + pub(super) fn lower_partly_resolved_path( + &mut self, + node: ExprOrPatId, + resolution: TypeNs, + resolved_segment: PathSegment<'_>, + remaining_segments: PathSegments<'_>, + resolved_segment_idx: u32, + infer_args: bool, + ) -> (Ty, Option<TypeNs>) { + let diagnostics = self.diagnostics; + self.ctx.lower_partly_resolved_path( + resolution, + resolved_segment, + remaining_segments, + resolved_segment_idx, + infer_args, + &mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }), + ) + } +} + +impl<'a> Deref for InferenceTyLoweringContext<'a> { + type Target = TyLoweringContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.ctx + } +} + +impl DerefMut for InferenceTyLoweringContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.ctx + } +} + +impl Drop for InferenceTyLoweringContext<'_> { + fn drop(&mut self) { + self.diagnostics + .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics)); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a13541be695..6b6c0348dcb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -206,7 +206,7 @@ impl InferenceContext<'_> { path, self.body.expr_path_hygiene(expr), ) - .map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), + .is_none_or(|res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), Expr::Underscore => true, Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, Expr::Field { .. } | Expr::Index { .. } => true, @@ -499,7 +499,7 @@ impl InferenceContext<'_> { // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here let is_varargs = - derefed_callee.callable_sig(self.db).map_or(false, |sig| sig.is_varargs) + derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { @@ -531,7 +531,7 @@ impl InferenceContext<'_> { (params, ret_ty) } None => { - self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction { + self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { call_expr: tgt_expr, found: callee_ty.clone(), }); @@ -707,7 +707,7 @@ impl InferenceContext<'_> { self.result.standard_types.never.clone() } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(path.as_deref(), false); + let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(&ty, &t); @@ -1816,9 +1816,10 @@ impl InferenceContext<'_> { if !is_public { if let Either::Left(field) = field_id { // FIXME: Merge this diagnostic into UnresolvedField? - self.result - .diagnostics - .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field }); + self.push_diagnostic(InferenceDiagnostic::PrivateField { + expr: tgt_expr, + field, + }); } } ty @@ -1835,7 +1836,7 @@ impl InferenceContext<'_> { VisibleFromModule::Filter(self.resolver.module()), name, ); - self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField { + self.push_diagnostic(InferenceDiagnostic::UnresolvedField { expr: tgt_expr, receiver: receiver_ty.clone(), name: name.clone(), @@ -1927,7 +1928,7 @@ impl InferenceContext<'_> { }, ); - self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall { + self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall { expr: tgt_expr, receiver: receiver_ty.clone(), name: method_name.clone(), @@ -2042,7 +2043,7 @@ impl InferenceContext<'_> { continue; } - while skip_indices.peek().map_or(false, |i| *i < idx as u32) { + while skip_indices.peek().is_some_and(|i| *i < idx as u32) { skip_indices.next(); } if skip_indices.peek().copied() == Some(idx as u32) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 50e761196ec..00398f019da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -35,7 +35,7 @@ impl InferenceContext<'_> { ellipsis: Option<u32>, subs: &[PatId], ) -> Ty { - let (ty, def) = self.resolve_variant(path, true); + let (ty, def) = self.resolve_variant(id.into(), path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); @@ -115,7 +115,7 @@ impl InferenceContext<'_> { id: PatId, subs: impl ExactSizeIterator<Item = (Name, PatId)>, ) -> Ty { - let (ty, def) = self.resolve_variant(path, false); + let (ty, def) = self.resolve_variant(id.into(), path, false); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index a6296c3af23..73bcefaf2a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -14,6 +14,7 @@ use crate::{ builder::ParamKind, consteval, error_lifetime, generics::generics, + infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, @@ -147,36 +148,38 @@ impl InferenceContext<'_> { path: &Path, id: ExprOrPatId, ) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> { + // Don't use `self.make_ty()` here as we need `orig_ns`. + let mut ctx = TyLoweringContext::new( + self.db, + &self.resolver, + &self.body.types, + self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, + ); let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { let last = path.segments().last()?; - // Don't use `self.make_ty()` here as we need `orig_ns`. - let mut ctx = crate::lower::TyLoweringContext::new( - self.db, - &self.resolver, - &self.body.types, - self.owner.into(), - ); let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); let ty = self.table.insert_type_vars(ty); let ty = self.table.normalize_associated_types_in(ty); let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.table.insert_type_vars(ty); let ty = self.table.normalize_associated_types_in(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { let hygiene = self.body.expr_or_pat_path_hygiene(id); // FIXME: report error, unresolved first path segment - let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?; + let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?; + drop(ctx); match value_or_partial { ResolveValueResult::ValueNs(it, _) => (it, None), ResolveValueResult::Partial(def, remaining_index, _) => self - .resolve_assoc_item(def, path, remaining_index, id) + .resolve_assoc_item(id, def, path, remaining_index, id) .map(|(it, substs)| (it, Some(substs)))?, } }; @@ -212,6 +215,7 @@ impl InferenceContext<'_> { fn resolve_assoc_item( &mut self, + node: ExprOrPatId, def: TypeNs, path: &Path, remaining_index: usize, @@ -260,17 +264,23 @@ impl InferenceContext<'_> { // as Iterator>::Item::default`) let remaining_segments_for_ty = remaining_segments.take(remaining_segments.len() - 1); - let (ty, _) = self.with_body_ty_lowering(|ctx| { - ctx.lower_partly_resolved_path( - def, - resolved_segment, - remaining_segments_for_ty, - true, - &mut |_, _reason| { - // FIXME: Report an error. - }, - ) - }); + let mut ctx = TyLoweringContext::new( + self.db, + &self.resolver, + &self.body.types, + self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, + ); + let (ty, _) = ctx.lower_partly_resolved_path( + node, + def, + resolved_segment, + remaining_segments_for_ty, + (remaining_index - 1) as u32, + true, + ); + drop(ctx); if ty.is_unknown() { return None; } @@ -305,13 +315,7 @@ impl InferenceContext<'_> { } AssocItemId::ConstId(konst) => { - if self - .db - .const_data(konst) - .name - .as_ref() - .map_or(false, |n| n == segment.name) - { + if self.db.const_data(konst).name.as_ref() == Some(segment.name) { Some(AssocItemId::ConstId(konst)) } else { None diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 165861c1b17..8a8992cf372 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -282,7 +282,7 @@ impl<'a> InferenceTable<'a> { let is_diverging = self .type_variable_table .get(iv.index() as usize) - .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)); + .is_some_and(|data| data.contains(TypeVariableFlags::DIVERGING)); if is_diverging { return TyKind::Never.intern(Interner); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 56b549436c7..d6039c548b6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -139,7 +139,7 @@ impl UninhabitedFrom<'_> { ty: &Binders<Ty>, subst: &Substitution, ) -> ControlFlow<VisiblyUninhabited> { - if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) { + if vis.is_none_or(|it| it.is_visible_from(self.db.upcast(), self.target_mod)) { let ty = ty.clone().substitute(Interner, subst); ty.visit_with(self, DebruijnIndex::INNERMOST) } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index f704b59d303..ff9c52fbb6c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -13,6 +13,7 @@ pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool { let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 0c1f63880cd..108171586ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -197,6 +197,7 @@ fn layout_of_simd_ty( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: 0, })) } @@ -313,6 +314,7 @@ pub fn layout_of_ty_query( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Slice(element) => { @@ -326,6 +328,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Str => Layout { @@ -337,6 +340,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index c1a67fcc407..0ba765bd75e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -113,7 +113,7 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, let get = |name| { let attr = attrs.by_key(name).tt_values(); for tree in attr { - if let Some(it) = tree.token_trees.first() { + if let Some(it) = tree.iter().next_as_view() { let text = it.to_string().replace('_', ""); let (text, base) = match text.as_bytes() { [b'0', b'x', ..] => (&text[2..], 16), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index f40d508f755..66159ddce2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; -use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot}; +use project_model::{toolchain_info::QueryConfig, Sysroot}; use rustc_hash::FxHashMap; use syntax::ToSmolStr; use test_fixture::WithFixture; @@ -17,8 +17,8 @@ use crate::{ mod closure; fn current_machine_data_layout() -> String { - project_model::target_data_layout::get( - RustcDataLayoutConfig::Rustc(&Sysroot::empty()), + project_model::toolchain_info::target_data_layout::get( + QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), ) @@ -241,31 +241,31 @@ fn recursive() { #[test] fn repr_packed() { size_and_align! { - #[repr(packed)] + #[repr(Rust, packed)] struct Goal; } size_and_align! { - #[repr(packed(2))] + #[repr(Rust, packed(2))] struct Goal; } size_and_align! { - #[repr(packed(4))] + #[repr(Rust, packed(4))] struct Goal; } size_and_align! { - #[repr(packed)] + #[repr(Rust, packed)] struct Goal(i32); } size_and_align! { - #[repr(packed(2))] + #[repr(Rust, packed(2))] struct Goal(i32); } size_and_align! { - #[repr(packed(4))] + #[repr(Rust, packed(4))] struct Goal(i32); } - check_size_and_align("#[repr(packed(5))] struct Goal(i32);", "", 4, 1); + check_size_and_align("#[repr(Rust, packed(5))] struct Goal(i32);", "", 4, 1); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 8bb90ca31e4..3c18ea92816 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -24,7 +24,6 @@ extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; mod builder; mod chalk_db; mod chalk_ext; -mod generics; mod infer; mod inhabitedness; mod interner; @@ -39,6 +38,7 @@ pub mod db; pub mod diagnostics; pub mod display; pub mod dyn_compatibility; +pub mod generics; pub mod lang_items; pub mod layout; pub mod method_resolution; @@ -50,6 +50,7 @@ pub mod traits; mod test_db; #[cfg(test)] mod tests; +mod variance; use std::hash::Hash; @@ -89,9 +90,8 @@ pub use infer::{ }; pub use interner::Interner; pub use lower::{ - associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode, - ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind, - ValueTyDefId, + associated_type_shorthand_candidates, diagnostics::*, ImplTraitLoweringMode, ParamLoweringMode, + TyDefId, TyLoweringContext, ValueTyDefId, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, @@ -101,6 +101,7 @@ pub use mapping::{ pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use variance::Variance; pub use chalk_ir::{ cast::Cast, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index b23f2749ab2..24f67fd6602 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -5,6 +5,8 @@ //! - Building the type for an item: This happens through the `ty` query. //! //! This usually involves resolving names, collecting generic arguments etc. +pub(crate) mod diagnostics; + use std::{ cell::OnceCell, iter, mem, @@ -21,8 +23,9 @@ use chalk_ir::{ use either::Either; use hir_def::{ + body::HygieneId, builtin_type::BuiltinType, - data::adt::StructKind, + data::{adt::StructKind, TraitFlags}, expander::Expander, generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, @@ -31,7 +34,7 @@ use hir_def::{ lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::{ ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, TypesMap, TypesSourceMap, @@ -59,6 +62,7 @@ use crate::{ db::HirDatabase, error_lifetime, generics::{generics, trait_self_param_idx, Generics}, + lower::diagnostics::*, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, @@ -102,31 +106,6 @@ impl ImplTraitLoweringState { } } -type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct TyLoweringDiagnostic { - pub source: TypeSource, - pub kind: TyLoweringDiagnosticKind, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum TyLoweringDiagnosticKind { - GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason }, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum GenericArgsProhibitedReason { - Module, - TyParam, - SelfTy, - PrimitiveTy, - /// When there is a generic enum, within the expression `Enum::Variant`, - /// either `Enum` or `Variant` are allowed to have generic arguments, but not both. - // FIXME: This is not used now but it should be. - EnumVariant, -} - #[derive(Debug)] pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, @@ -536,8 +515,8 @@ impl<'a> TyLoweringContext<'a> { /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. - fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> { - let type_ref = &self.types_map[type_ref]; + fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option<TypeOrConstParamId> { + let type_ref = &self.types_map[type_ref_id]; let path = match type_ref { TypeRef::Path(path) => path, _ => return None, @@ -548,8 +527,10 @@ impl<'a> TyLoweringContext<'a> { if path.segments().len() > 1 { return None; } - let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, None, _)) => it, + let resolution = match self + .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id)) + { + Some((it, None)) => it, _ => return None, }; match resolution { @@ -584,11 +565,9 @@ impl<'a> TyLoweringContext<'a> { resolution: TypeNs, resolved_segment: PathSegment<'_>, remaining_segments: PathSegments<'_>, + _resolved_segment_idx: u32, infer_args: bool, - on_prohibited_generics_for_resolved_segment: &mut dyn FnMut( - &mut Self, - GenericArgsProhibitedReason, - ), + _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), ) -> (Ty, Option<TypeNs>) { let ty = match resolution { TypeNs::TraitId(trait_) => { @@ -655,44 +634,28 @@ impl<'a> TyLoweringContext<'a> { // FIXME(trait_alias): Implement trait alias. return (TyKind::Error.intern(Interner), None); } - TypeNs::GenericParam(param_id) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::TyParam, - ); + TypeNs::GenericParam(param_id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) } + ParamLoweringMode::Variable => { + let idx = match self + .generics() + .expect("generics in scope") + .type_or_const_param_idx(param_id.into()) + { + None => { + never!("no matching generics"); + return (TyKind::Error.intern(Interner), None); + } + Some(idx) => idx, + }; - match self.type_param_mode { - ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) - } - ParamLoweringMode::Variable => { - let idx = match self - .generics() - .expect("generics in scope") - .type_or_const_param_idx(param_id.into()) - { - None => { - never!("no matching generics"); - return (TyKind::Error.intern(Interner), None); - } - Some(idx) => idx, - }; - - TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) - } + TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) } - .intern(Interner) } + .intern(Interner), TypeNs::SelfType(impl_id) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::SelfTy, - ); - } - let generics = self.generics().expect("impl should have generic param scope"); match self.type_param_mode { @@ -718,13 +681,6 @@ impl<'a> TyLoweringContext<'a> { } } TypeNs::AdtSelfType(adt) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::SelfTy, - ); - } - let generics = generics(self.db.upcast(), adt.into()); let substs = match self.type_param_mode { ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db), @@ -737,12 +693,6 @@ impl<'a> TyLoweringContext<'a> { TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args), TypeNs::BuiltinType(it) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::PrimitiveTy, - ); - } self.lower_path_inner(resolved_segment, it.into(), infer_args) } TypeNs::TypeAliasId(it) => { @@ -754,6 +704,220 @@ impl<'a> TyLoweringContext<'a> { self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) } + fn handle_type_ns_resolution( + &mut self, + resolution: &TypeNs, + resolved_segment: PathSegment<'_>, + resolved_segment_idx: usize, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) { + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx as u32, + reason, + }, + ); + } + }; + + match resolution { + TypeNs::SelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) + } + TypeNs::AdtSelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::BuiltinType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) + } + TypeNs::AdtId(_) + | TypeNs::EnumVariantId(_) + | TypeNs::TypeAliasId(_) + | TypeNs::TraitId(_) + | TypeNs::TraitAliasId(_) => {} + } + } + + pub(crate) fn resolve_path_in_type_ns_fully( + &mut self, + path: &Path, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option<TypeNs> { + let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?; + if unresolved.is_some() { + return None; + } + Some(res) + } + + pub(crate) fn resolve_path_in_type_ns( + &mut self, + path: &Path, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option<(TypeNs, Option<usize>)> { + let (resolution, remaining_index, _) = + self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?; + let segments = path.segments(); + + match path { + // `segments.is_empty()` can occur with `self`. + Path::Normal(..) if !segments.is_empty() => (), + _ => return Some((resolution, remaining_index)), + }; + + let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index { + None => ( + segments.strip_last(), + segments.len() - 1, + segments.last().expect("resolved path has at least one element"), + ), + Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()), + }; + + for (i, mod_segment) in module_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }, + ); + } + } + + self.handle_type_ns_resolution( + &resolution, + resolved_segment, + resolved_segment_idx, + on_diagnostic, + ); + + Some((resolution, remaining_index)) + } + + pub(crate) fn resolve_path_in_value_ns( + &mut self, + path: &Path, + hygiene_id: HygieneId, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option<ResolveValueResult> { + let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info( + self.db.upcast(), + path, + hygiene_id, + )?; + + let segments = path.segments(); + match path { + // `segments.is_empty()` can occur with `self`. + Path::Normal(..) if !segments.is_empty() => (), + _ => return Some(res), + }; + + let (mod_segments, enum_segment) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2)) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None), + }; + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }, + ); + } + } + + if let Some(enum_segment) = enum_segment { + if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }, + ); + } + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = + segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}")); + let resolved_segment = segments.last().unwrap(); + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx as u32, + reason, + }, + ); + } + }; + + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} + ValueNs::LocalBinding(_) => {} + } + } + ResolveValueResult::Partial(resolution, unresolved_idx, _) => { + let resolved_segment_idx = unresolved_idx - 1; + let resolved_segment = segments.get(resolved_segment_idx).unwrap(); + self.handle_type_ns_resolution( + resolution, + resolved_segment, + resolved_segment_idx, + on_diagnostic, + ); + } + }; + Some(res) + } + + fn on_path_diagnostic_callback( + type_ref: TypeRefId, + ) -> impl FnMut(&mut Self, PathLoweringDiagnostic) { + move |this, diag| { + this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + } + } + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) { // Resolve the path (in type namespace) if let Some(type_ref) = path.type_anchor() { @@ -761,11 +925,13 @@ impl<'a> TyLoweringContext<'a> { return self.lower_ty_relative_path(ty, res, path.segments()); } - let (resolution, remaining_index, _) = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some(it) => it, - None => return (TyKind::Error.intern(Interner), None), - }; + let (resolution, remaining_index) = match self.resolve_path_in_type_ns( + path, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), + ) { + Some(it) => it, + None => return (TyKind::Error.intern(Interner), None), + }; if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { // trait object type without dyn @@ -774,38 +940,22 @@ impl<'a> TyLoweringContext<'a> { return (ty, None); } - let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) = - match remaining_index { - None => ( - path.segments().strip_last(), - path.segments().len() - 1, - path.segments().last().expect("resolved path has at least one element"), - PathSegments::EMPTY, - ), - Some(i) => ( - path.segments().take(i - 1), - i - 1, - path.segments().get(i - 1).unwrap(), - path.segments().skip(i), - ), - }; - - self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module); + let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index { + None => ( + path.segments().len() - 1, + path.segments().last().expect("resolved path has at least one element"), + PathSegments::EMPTY, + ), + Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)), + }; self.lower_partly_resolved_path( resolution, resolved_segment, remaining_segments, + resolved_segment_idx as u32, false, - &mut |this, reason| { - this.push_diagnostic( - path_id.type_ref(), - TyLoweringDiagnosticKind::GenericArgsProhibited { - segment: resolved_segment_idx as u32, - reason, - }, - ) - }, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), ) } @@ -1107,7 +1257,9 @@ impl<'a> TyLoweringContext<'a> { if segment.args_and_bindings.is_some() { self.push_diagnostic( path_id.type_ref(), - TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason }, + TyLoweringDiagnosticKind::PathDiagnostic( + PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason }, + ), ); } }); @@ -1119,7 +1271,10 @@ impl<'a> TyLoweringContext<'a> { explicit_self_ty: Ty, ) -> Option<TraitRef> { let path = &self.types_map[path_id]; - let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { + let resolved = match self.resolve_path_in_type_ns_fully( + path, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), + )? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, _ => return None, @@ -1412,9 +1567,17 @@ impl<'a> TyLoweringContext<'a> { match (lhs.skip_binders(), rhs.skip_binders()) { (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => { let lhs_id = lhs.trait_id; - let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto; + let lhs_is_auto = ctx + .db + .trait_data(from_chalk_trait_id(lhs_id)) + .flags + .contains(TraitFlags::IS_AUTO); let rhs_id = rhs.trait_id; - let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto; + let rhs_is_auto = ctx + .db + .trait_data(from_chalk_trait_id(rhs_id)) + .flags + .contains(TraitFlags::IS_AUTO); if !lhs_is_auto && !rhs_is_auto { multiple_regular_traits = true; @@ -2588,14 +2751,14 @@ fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Intern crate::fold_free_vars( s, |bound, binders| { - if bound.index_if_innermost().map_or(true, is_allowed) { + if bound.index_if_innermost().is_none_or(is_allowed) { bound.shifted_in_from(binders).to_ty(Interner) } else { TyKind::Error.intern(Interner) } }, |ty, bound, binders| { - if bound.index_if_innermost().map_or(true, is_allowed) { + if bound.index_if_innermost().is_none_or(is_allowed) { bound.shifted_in_from(binders).to_const(Interner, ty) } else { unknown_const(ty) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs new file mode 100644 index 00000000000..7fe196cdbb5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs @@ -0,0 +1,36 @@ +//! This files contains the declaration of diagnostics kinds for ty and path lowering. + +use either::Either; +use hir_def::type_ref::TypeRefId; + +type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TyLoweringDiagnostic { + pub source: TypeSource, + pub kind: TyLoweringDiagnosticKind, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TyLoweringDiagnosticKind { + PathDiagnostic(PathLoweringDiagnostic), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GenericArgsProhibitedReason { + Module, + TyParam, + SelfTy, + PrimitiveTy, + Const, + Static, + /// When there is a generic enum, within the expression `Enum::Variant`, + /// either `Enum` or `Variant` are allowed to have generic arguments, but not both. + // FIXME: This is not used now but it should be. + EnumVariant, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum PathLoweringDiagnostic { + GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason }, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 5a72b97653d..62b071b2f32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -7,7 +7,7 @@ use std::ops::ControlFlow; use base_db::CrateId; use chalk_ir::{cast::Cast, UniverseIndex, WithKind}; use hir_def::{ - data::{adt::StructFlags, ImplData}, + data::{adt::StructFlags, ImplData, TraitFlags}, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, @@ -377,7 +377,7 @@ pub(crate) fn incoherent_inherent_impl_crates( for krate in crate_graph.transitive_deps(krate) { let impls = db.inherent_impls_in_crate(krate); - if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { + if impls.map.get(&fp).is_some_and(|v| !v.is_empty()) { res.push(krate); } } @@ -419,11 +419,17 @@ pub fn def_crates( } TyKind::Dyn(_) => { let trait_id = ty.dyn_trait()?; - Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls { - db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id)) - } else { - smallvec![trait_id.module(db.upcast()).krate()] - }) + Some( + if db + .trait_data(trait_id) + .flags + .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) + { + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id)) + } else { + smallvec![trait_id.module(db.upcast()).krate()] + }, + ) } // for primitives, there may be impls in various places (core and alloc // mostly). We just check the whole crate graph for crates with impls @@ -805,8 +811,8 @@ fn is_inherent_impl_coherent( | TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(), &TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(), - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate() == def_map.krate() + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + from_chalk_trait_id(trait_id).module(db.upcast()).krate() == def_map.krate() }), _ => true, @@ -834,9 +840,10 @@ fn is_inherent_impl_coherent( .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls, }, - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - db.trait_data(from_chalk_trait_id(trait_ref.trait_id)) - .rustc_has_incoherent_inherent_impls + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + db.trait_data(from_chalk_trait_id(trait_id)) + .flags + .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) }), _ => false, @@ -896,8 +903,8 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { match unwrap_fundamental(ty).kind(Interner) { &TyKind::Adt(AdtId(id), _) => is_local(id.module(db.upcast()).krate()), TyKind::Error => true, - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - is_local(from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()) + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + is_local(from_chalk_trait_id(trait_id).module(db.upcast()).krate()) }), _ => false, } @@ -914,7 +921,7 @@ pub fn iterate_path_candidates( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { iterate_method_candidates_dyn( ty, @@ -925,7 +932,7 @@ pub fn iterate_path_candidates( name, LookupMode::Path, // the adjustments are not relevant for path lookup - &mut |_, id, _| callback(id), + callback, ) } @@ -937,7 +944,7 @@ pub fn iterate_method_candidates_dyn( visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let _p = tracing::info_span!( "iterate_method_candidates_dyn", @@ -1007,7 +1014,7 @@ fn iterate_method_candidates_with_autoref( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { // don't try to resolve methods on unknown types @@ -1022,7 +1029,7 @@ fn iterate_method_candidates_with_autoref( traits_in_scope, visible_from_module, name, - &mut callback, + callback, ) }; @@ -1052,6 +1059,45 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) } +pub trait MethodCandidateCallback { + fn on_inherent_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()>; + + fn on_trait_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()>; +} + +impl<F> MethodCandidateCallback for F +where + F: FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, +{ + fn on_inherent_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()> { + self(adjustments, item, is_visible) + } + + fn on_trait_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()> { + self(adjustments, item, is_visible) + } +} + #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver( table: &mut InferenceTable<'_>, @@ -1060,7 +1106,7 @@ fn iterate_method_candidates_by_receiver( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could @@ -1076,7 +1122,9 @@ fn iterate_method_candidates_by_receiver( Some(&receiver_ty), Some(receiver_adjustments.clone()), visible_from_module, - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_inherent_method(adjustments, item, is_visible) + }, )? } ControlFlow::Continue(()) @@ -1096,7 +1144,9 @@ fn iterate_method_candidates_by_receiver( name, Some(&receiver_ty), Some(receiver_adjustments.clone()), - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_trait_method(adjustments, item, is_visible) + }, )? } ControlFlow::Continue(()) @@ -1111,7 +1161,7 @@ fn iterate_method_candidates_for_self_ty( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); let self_ty = table.instantiate_canonical(self_ty.clone()); @@ -1122,7 +1172,9 @@ fn iterate_method_candidates_for_self_ty( None, None, visible_from_module, - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_inherent_method(adjustments, item, is_visible) + }, )?; iterate_trait_method_candidates( &self_ty, @@ -1131,7 +1183,9 @@ fn iterate_method_candidates_for_self_ty( name, None, None, - callback, + &mut |adjustments, item, is_visible| { + callback.on_trait_method(adjustments, item, is_visible) + }, ) } @@ -1158,7 +1212,7 @@ fn iterate_trait_method_candidates( // 2021. // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for // arrays. - if data.skip_array_during_method_dispatch + if data.flags.contains(TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH) && matches!(self_ty.kind(Interner), TyKind::Array(..)) { // FIXME: this should really be using the edition of the method name's span, in case it @@ -1167,7 +1221,7 @@ fn iterate_trait_method_candidates( continue; } } - if data.skip_boxed_slice_during_method_dispatch + if data.flags.contains(TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) && matches!( self_ty.kind(Interner), TyKind::Adt(AdtId(def), subst) if is_box(table.db, *def) @@ -1427,7 +1481,7 @@ fn is_valid_impl_method_candidate( AssocItemId::ConstId(c) => { let db = table.db; check_that!(receiver_ty.is_none()); - check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); + check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n))); if let Some(from_module) = visible_from_module { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { @@ -1465,7 +1519,7 @@ fn is_valid_trait_method_candidate( AssocItemId::FunctionId(fn_id) => { let data = db.function_data(fn_id); - check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(name.is_none_or(|n| n == &data.name)); table.run_in_snapshot(|table| { let impl_subst = TyBuilder::subst_for_def(db, trait_id, None) @@ -1494,7 +1548,7 @@ fn is_valid_trait_method_candidate( } AssocItemId::ConstId(c) => { check_that!(receiver_ty.is_none()); - check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); + check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n))); IsValidCandidate::Yes } @@ -1515,7 +1569,7 @@ fn is_valid_impl_fn_candidate( let db = table.db; let data = db.function_data(fn_id); - check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(name.is_none_or(|n| n == &data.name)); if let Some(from_module) = visible_from_module { if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) { cov_mark::hit!(autoderef_candidate_not_visible); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e3072d6ee79..dcae6877ba8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1211,7 +1211,9 @@ impl Evaluator<'_> { } lc = &lc[..self.ptr_size()]; rc = &rc[..self.ptr_size()]; - ls + lc = self.read_memory(Address::from_bytes(lc)?, ls)?; + rc = self.read_memory(Address::from_bytes(rc)?, ls)?; + break 'binary_op Owned(vec![u8::from(lc == rc)]); } else { self.size_of_sized(&ty, locals, "operand of binary op")? }; @@ -1340,18 +1342,8 @@ impl Evaluator<'_> { } } else { let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); - let l128 = i128::from_le_bytes(pad16(lc, is_signed)); - let r128 = i128::from_le_bytes(pad16(rc, is_signed)); - let check_overflow = |r: i128| { - // FIXME: this is not very correct, and only catches the basic cases. - let r = r.to_le_bytes(); - for &k in &r[lc.len()..] { - if k != 0 && (k != 255 || !is_signed) { - return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); - } - } - Ok(Owned(r[0..lc.len()].into())) - }; + let l128 = IntValue::from_bytes(lc, is_signed); + let r128 = IntValue::from_bytes(rc, is_signed); match op { BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { let r = op.run_compare(l128, r128) as u8; @@ -1366,25 +1358,31 @@ impl Evaluator<'_> { | BinOp::Rem | BinOp::Sub => { let r = match op { - BinOp::Add => l128.overflowing_add(r128).0, - BinOp::Mul => l128.overflowing_mul(r128).0, + BinOp::Add => l128.checked_add(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Mul => l128.checked_mul(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::Div => l128.checked_div(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, - BinOp::Sub => l128.overflowing_sub(r128).0, + BinOp::Sub => l128.checked_sub(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::BitAnd => l128 & r128, BinOp::BitOr => l128 | r128, BinOp::BitXor => l128 ^ r128, _ => unreachable!(), }; - check_overflow(r)? + Owned(r.to_bytes()) } BinOp::Shl | BinOp::Shr => { let r = 'b: { - if let Ok(shift_amount) = u32::try_from(r128) { + if let Some(shift_amount) = r128.as_u32() { let r = match op { BinOp::Shl => l128.checked_shl(shift_amount), BinOp::Shr => l128.checked_shr(shift_amount), @@ -1401,7 +1399,7 @@ impl Evaluator<'_> { }; return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); }; - Owned(r.to_le_bytes()[..lc.len()].to_vec()) + Owned(r.to_bytes()) } BinOp::Offset => not_supported!("offset binop"), } @@ -2115,7 +2113,7 @@ impl Evaluator<'_> { while self.heap.len() % align != 0 { self.heap.push(0); } - if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) { + if size.checked_add(self.heap.len()).is_none_or(|x| x > self.memory_limit) { return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed"))); } let pos = self.heap.len(); @@ -2974,3 +2972,129 @@ pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { res[..it.len()].copy_from_slice(it); res } + +macro_rules! for_each_int_type { + ($call_macro:path, $args:tt) => { + $call_macro! { + $args + I8 + U8 + I16 + U16 + I32 + U32 + I64 + U64 + I128 + U128 + } + }; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum IntValue { + I8(i8), + U8(u8), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(i64), + U64(u64), + I128(i128), + U128(u128), +} + +macro_rules! checked_int_op { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, other: Self) -> Option<Self> { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => a.$op(b).map(Self::$int_ty), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +macro_rules! int_bit_shifts { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, amount: u32) -> Option<Self> { + match self { + $( Self::$int_ty(this) => this.$op(amount).map(Self::$int_ty), )+ + } + } + }; +} + +macro_rules! unchecked_int_op { + ( [ $name:ident, $op:tt ] $( $int_ty:ident )+ ) => { + fn $name(self, other: Self) -> Self { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => Self::$int_ty(a $op b), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +impl IntValue { + fn from_bytes(bytes: &[u8], is_signed: bool) -> Self { + match (bytes.len(), is_signed) { + (1, false) => Self::U8(u8::from_le_bytes(bytes.try_into().unwrap())), + (1, true) => Self::I8(i8::from_le_bytes(bytes.try_into().unwrap())), + (2, false) => Self::U16(u16::from_le_bytes(bytes.try_into().unwrap())), + (2, true) => Self::I16(i16::from_le_bytes(bytes.try_into().unwrap())), + (4, false) => Self::U32(u32::from_le_bytes(bytes.try_into().unwrap())), + (4, true) => Self::I32(i32::from_le_bytes(bytes.try_into().unwrap())), + (8, false) => Self::U64(u64::from_le_bytes(bytes.try_into().unwrap())), + (8, true) => Self::I64(i64::from_le_bytes(bytes.try_into().unwrap())), + (16, false) => Self::U128(u128::from_le_bytes(bytes.try_into().unwrap())), + (16, true) => Self::I128(i128::from_le_bytes(bytes.try_into().unwrap())), + _ => panic!("invalid integer size"), + } + } + + fn to_bytes(self) -> Vec<u8> { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.to_le_bytes().to_vec() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + fn as_u32(self) -> Option<u32> { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.try_into().ok() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + for_each_int_type!(checked_int_op, [checked_add]); + for_each_int_type!(checked_int_op, [checked_sub]); + for_each_int_type!(checked_int_op, [checked_div]); + for_each_int_type!(checked_int_op, [checked_rem]); + for_each_int_type!(checked_int_op, [checked_mul]); + + for_each_int_type!(int_bit_shifts, [checked_shl]); + for_each_int_type!(int_bit_shifts, [checked_shr]); +} + +impl std::ops::BitAnd for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitand, &]); +} +impl std::ops::BitOr for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitor, |]); +} +impl std::ops::BitXor for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitxor, ^]); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 30d11373732..ce43e90df7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -879,3 +879,32 @@ fn main() { "#, ); } + +#[test] +fn long_str_eq_same_prefix() { + check_pass_and_stdio( + r#" +//- minicore: slice, index, coerce_unsized + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn write(fd: i32, buf: *const u8, count: usize) -> usize; +} + +fn main() { + // More than 16 bytes, the size of `i128`. + let long_str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"; + let output = match long_str { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => b"true" as &[u8], + _ => b"false", + }; + write(1, &output[0], output.len()); +} + "#, + "false", + "", + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index cabeeea2bd8..b7607b5f639 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -127,7 +127,15 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) + }); } defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { @@ -375,7 +383,15 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<DefWithBodyId> = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) + }); defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { let loc = it.lookup(&db); @@ -405,11 +421,11 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { buf } -fn visit_module( +pub(crate) fn visit_module( db: &TestDB, crate_def_map: &DefMap, module_id: LocalModuleId, - cb: &mut dyn FnMut(DefWithBodyId), + cb: &mut dyn FnMut(ModuleDefId), ) { visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb); for impl_id in crate_def_map[module_id].scope.impls() { @@ -417,18 +433,18 @@ fn visit_module( for &item in impl_data.items.iter() { match item { AssocItemId::FunctionId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); } AssocItemId::ConstId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); } - AssocItemId::TypeAliasId(_) => (), + AssocItemId::TypeAliasId(it) => { + cb(it.into()); + } } } } @@ -437,33 +453,27 @@ fn visit_module( db: &TestDB, crate_def_map: &DefMap, scope: &ItemScope, - cb: &mut dyn FnMut(DefWithBodyId), + cb: &mut dyn FnMut(ModuleDefId), ) { for decl in scope.declarations() { + cb(decl); match decl { ModuleDefId::FunctionId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::ConstId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::StaticId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { db.enum_data(it).variants.iter().for_each(|&(it, _)| { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); }); } @@ -473,7 +483,7 @@ fn visit_module( match item { AssocItemId::FunctionId(it) => cb(it.into()), AssocItemId::ConstId(it) => cb(it.into()), - AssocItemId::TypeAliasId(_) => (), + AssocItemId::TypeAliasId(it) => cb(it.into()), } } } @@ -483,7 +493,7 @@ fn visit_module( } } - fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) { + fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(ModuleDefId)) { for (_, def_map) in body.blocks(db) { for (mod_id, _) in def_map.modules() { visit_module(db, &def_map, mod_id, cb); @@ -553,7 +563,13 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); let new_text = " @@ -586,6 +602,12 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index b63d632dd26..7de92d6b160 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -24,6 +24,13 @@ fn check_closure_captures(ra_fixture: &str, expect: Expect) { let mut captures_info = Vec::new(); for def in defs { + let def = match def { + hir_def::ModuleDefId::FunctionId(it) => it.into(), + hir_def::ModuleDefId::EnumVariantId(it) => it.into(), + hir_def::ModuleDefId::ConstId(it) => it.into(), + hir_def::ModuleDefId::StaticId(it) => it.into(), + _ => continue, + }; let infer = db.infer(def); let db = &db; captures_info.extend(infer.closure_info.iter().flat_map(|(closure_id, (captures, _))| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0a24eeb1fe8..3757d722ac8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,4 +1,5 @@ use base_db::SourceDatabaseFileInputExt as _; +use hir_def::ModuleDefId; use test_fixture::WithFixture; use crate::{db::HirDatabase, test_db::TestDB}; @@ -19,7 +20,9 @@ fn foo() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").contains("infer")) @@ -39,7 +42,9 @@ fn foo() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(!format!("{events:?}").contains("infer"), "{events:#?}") @@ -66,7 +71,9 @@ fn baz() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").contains("infer")) @@ -91,7 +98,9 @@ fn baz() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").matches("infer").count() == 1, "{events:#?}") diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 42e7edaf0f4..bf7892f69bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -270,17 +270,15 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { return true; } - let is_intrinsic = db.attrs(func.into()).by_key(&sym::rustc_intrinsic).exists() - || data.abi.as_ref() == Some(&sym::rust_dash_intrinsic); - let loc = func.lookup(db.upcast()); match loc.container { hir_def::ItemContainerId::ExternBlockId(block) => { - if is_intrinsic || { - let id = block.lookup(db.upcast()).id; - id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic) - } { - // Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute + let id = block.lookup(db.upcast()).id; + let is_intrinsic_block = + id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic); + if is_intrinsic_block { + // legacy intrinsics + // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() } else { // Function in an `extern` block are always unsafe to call, except when @@ -288,7 +286,6 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { !data.is_safe() } } - _ if is_intrinsic => !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists(), _ => false, } } @@ -376,7 +373,7 @@ impl OpaqueInternableThing for InTypeConstIdMetadata { } fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool { - other.as_any().downcast_ref::<Self>().map_or(false, |x| self == x) + other.as_any().downcast_ref::<Self>() == Some(self) } fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs new file mode 100644 index 00000000000..30711b16dfb --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -0,0 +1,1065 @@ +//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] +//! chapter for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html +//! +//! The implementation here differs from rustc. Rustc does a crate wide fixpoint resolution +//! as the algorithm for determining variance is a fixpoint computation with potential cycles that +//! need to be resolved. rust-analyzer does not want a crate-wide analysis though as that would hurt +//! incrementality too much and as such our query is based on a per item basis. +//! +//! This does unfortunately run into the issue that we can run into query cycles which salsa +//! currently does not allow to be resolved via a fixpoint computation. This will likely be resolved +//! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach +//! while installing firewall per item queries to prevent invalidation issues. + +use crate::db::HirDatabase; +use crate::generics::{generics, Generics}; +use crate::{ + AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, + LifetimeData, Ty, TyKind, +}; +use base_db::ra_salsa::Cycle; +use chalk_ir::Mutability; +use hir_def::data::adt::StructFlags; +use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; +use std::fmt; +use std::ops::Not; +use stdx::never; +use triomphe::Arc; + +pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Arc<[Variance]>> { + tracing::debug!("variances_of(def={:?})", def); + match def { + GenericDefId::FunctionId(_) => (), + GenericDefId::AdtId(adt) => { + if let AdtId::StructId(id) = adt { + let flags = &db.struct_data(id).flags; + if flags.contains(StructFlags::IS_UNSAFE_CELL) { + return Some(Arc::from_iter(vec![Variance::Invariant; 1])); + } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { + return Some(Arc::from_iter(vec![Variance::Covariant; 1])); + } + } + } + _ => return None, + } + + let generics = generics(db.upcast(), def); + let count = generics.len(); + if count == 0 { + return None; + } + let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); + + variances.is_empty().not().then(|| Arc::from_iter(variances)) +} + +pub(crate) fn variances_of_cycle( + db: &dyn HirDatabase, + _cycle: &Cycle, + def: &GenericDefId, +) -> Option<Arc<[Variance]>> { + let generics = generics(db.upcast(), *def); + let count = generics.len(); + + if count == 0 { + return None; + } + Some(Arc::from(vec![Variance::Bivariant; count])) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Variance { + Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type + Invariant, // T<A> <: T<B> iff B == A -- e.g., type of mutable cell + Contravariant, // T<A> <: T<B> iff B <: A -- e.g., function param type + Bivariant, // T<A> <: T<B> -- e.g., unused type parameter +} + +impl fmt::Display for Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Variance::Covariant => write!(f, "covariant"), + Variance::Invariant => write!(f, "invariant"), + Variance::Contravariant => write!(f, "contravariant"), + Variance::Bivariant => write!(f, "bivariant"), + } + } +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// ```ignore (illustrative) + /// *mut Vec<i32> + /// ``` + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec<T>` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// ```ignore (illustrative) + /// fn(*const Vec<i32>, *mut Vec<i32) + /// ``` + /// The ambient variance is covariant. A `fn` type is + /// contravariant with respect to its parameters, so the variance + /// within which both pointer types appear is + /// `Covariant.xform(Contravariant)`, or `Contravariant`. `*const + /// T` is covariant with respect to `T`, so the variance within + /// which the first `Vec<i32>` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec<i32>` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec<i32>` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + fn xform(self, v: Variance) -> Variance { + match (self, v) { + // Figure 1, column 1. + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, + (Variance::Covariant, Variance::Invariant) => Variance::Invariant, + (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 2. + (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, + (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, + (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 3. + (Variance::Invariant, _) => Variance::Invariant, + + // Figure 1, column 4. + (Variance::Bivariant, _) => Variance::Bivariant, + } + } + + fn glb(self, v: Variance) -> Variance { + // Greatest lower bound of the variance lattice as + // defined in The Paper: + // + // * + // - + + // o + match (self, v) { + (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + + (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, + + (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, + } + } + + pub fn invariant(self) -> Self { + self.xform(Variance::Invariant) + } + + pub fn covariant(self) -> Self { + self.xform(Variance::Covariant) + } + + pub fn contravariant(self) -> Self { + self.xform(Variance::Contravariant) + } +} + +struct Context<'db> { + db: &'db dyn HirDatabase, + generics: Generics, + variances: Vec<Variance>, +} + +impl Context<'_> { + fn solve(mut self) -> Vec<Variance> { + tracing::debug!("solve(generics={:?})", self.generics); + match self.generics.def() { + GenericDefId::AdtId(adt) => { + let db = self.db; + let mut add_constraints_from_variant = |variant| { + let subst = self.generics.placeholder_subst(db); + for (_, field) in db.field_types(variant).iter() { + self.add_constraints_from_ty( + &field.clone().substitute(Interner, &subst), + Variance::Covariant, + ); + } + }; + match adt { + AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), + AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), + AdtId::EnumId(e) => { + db.enum_data(e).variants.iter().for_each(|&(variant, _)| { + add_constraints_from_variant(VariantId::EnumVariantId(variant)) + }); + } + } + } + GenericDefId::FunctionId(f) => { + let subst = self.generics.placeholder_subst(self.db); + self.add_constraints_from_sig( + self.db + .callable_item_signature(f.into()) + .substitute(Interner, &subst) + .params_and_return + .iter(), + Variance::Covariant, + ); + } + _ => {} + } + let mut variances = self.variances; + + // Const parameters are always invariant. + // Make all const parameters invariant. + for (idx, param) in self.generics.iter_id().enumerate() { + if let GenericParamId::ConstParamId(_) = param { + variances[idx] = Variance::Invariant; + } + } + + // Functions are permitted to have unused generic parameters: make those invariant. + if let GenericDefId::FunctionId(_) = self.generics.def() { + variances + .iter_mut() + .filter(|&&mut v| v == Variance::Bivariant) + .for_each(|v| *v = Variance::Invariant); + } + + variances + } + + /// Adds constraints appropriate for an instance of `ty` appearing + /// in a context with the generics defined in `generics` and + /// ambient variance `variance` + fn add_constraints_from_ty(&mut self, ty: &Ty, variance: Variance) { + tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); + match ty.kind(Interner) { + TyKind::Scalar(_) | TyKind::Never | TyKind::Str | TyKind::Foreign(..) => { + // leaf type -- noop + } + TyKind::FnDef(..) | TyKind::Coroutine(..) | TyKind::Closure(..) => { + never!("Unexpected unnameable type in variance computation: {:?}", ty); + } + TyKind::Ref(mutbl, lifetime, ty) => { + self.add_constraints_from_region(lifetime, variance); + self.add_constraints_from_mt(ty, *mutbl, variance); + } + TyKind::Array(typ, len) => { + self.add_constraints_from_const(len, variance); + self.add_constraints_from_ty(typ, variance); + } + TyKind::Slice(typ) => { + self.add_constraints_from_ty(typ, variance); + } + TyKind::Raw(mutbl, ty) => { + self.add_constraints_from_mt(ty, *mutbl, variance); + } + TyKind::Tuple(_, subtys) => { + for subty in subtys.type_parameters(Interner) { + self.add_constraints_from_ty(&subty, variance); + } + } + TyKind::Adt(def, args) => { + self.add_constraints_from_args(def.0.into(), args.as_slice(Interner), variance); + } + TyKind::Alias(AliasTy::Opaque(opaque)) => { + self.add_constraints_from_invariant_args( + opaque.substitution.as_slice(Interner), + variance, + ); + } + TyKind::Alias(AliasTy::Projection(proj)) => { + self.add_constraints_from_invariant_args( + proj.substitution.as_slice(Interner), + variance, + ); + } + // FIXME: check this + TyKind::AssociatedType(_, subst) => { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + // FIXME: check this + TyKind::OpaqueType(_, subst) => { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + TyKind::Dyn(it) => { + // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`: + self.add_constraints_from_region(&it.lifetime, variance); + + if let Some(trait_ref) = it.principal() { + // Trait are always invariant so we can take advantage of that. + self.add_constraints_from_invariant_args( + trait_ref + .map(|it| it.map(|it| it.substitution.clone())) + .substitute( + Interner, + &[GenericArg::new( + Interner, + chalk_ir::GenericArgData::Ty(TyKind::Error.intern(Interner)), + )], + ) + .skip_binders() + .as_slice(Interner), + variance, + ); + } + + // FIXME + // for projection in data.projection_bounds() { + // match projection.skip_binder().term.unpack() { + // TyKind::TermKind::Ty(ty) => { + // self.add_constraints_from_ty( ty, self.invariant); + // } + // TyKind::TermKind::Const(c) => { + // self.add_constraints_from_const( c, self.invariant) + // } + // } + // } + } + + // Chalk has no params, so use placeholders for now? + TyKind::Placeholder(index) => { + let idx = crate::from_placeholder_idx(self.db, *index); + let index = self.generics.type_or_const_param_idx(idx).unwrap(); + self.constrain(index, variance); + } + TyKind::Function(f) => { + self.add_constraints_from_sig( + f.substitution.0.iter(Interner).filter_map(move |p| p.ty(Interner)), + variance, + ); + } + TyKind::Error => { + // we encounter this when walking the trait references for object + // types, where we use Error as the Self type + } + TyKind::CoroutineWitness(..) | TyKind::BoundVar(..) | TyKind::InferenceVar(..) => { + never!("unexpected type encountered in variance inference: {:?}", ty) + } + } + } + + fn add_constraints_from_invariant_args(&mut self, args: &[GenericArg], variance: Variance) { + let variance_i = variance.invariant(); + + for k in args { + match k.data(Interner) { + GenericArgData::Lifetime(lt) => self.add_constraints_from_region(lt, variance_i), + GenericArgData::Ty(ty) => self.add_constraints_from_ty(ty, variance_i), + GenericArgData::Const(val) => self.add_constraints_from_const(val, variance_i), + } + } + } + + /// Adds constraints appropriate for a nominal type (enum, struct, + /// object, etc) appearing in a context with ambient variance `variance` + fn add_constraints_from_args( + &mut self, + def_id: GenericDefId, + args: &[GenericArg], + variance: Variance, + ) { + // We don't record `inferred_starts` entries for empty generics. + if args.is_empty() { + return; + } + let Some(variances) = self.db.variances_of(def_id) else { + return; + }; + + for (i, k) in args.iter().enumerate() { + match k.data(Interner) { + GenericArgData::Lifetime(lt) => { + self.add_constraints_from_region(lt, variance.xform(variances[i])) + } + GenericArgData::Ty(ty) => { + self.add_constraints_from_ty(ty, variance.xform(variances[i])) + } + GenericArgData::Const(val) => self.add_constraints_from_const(val, variance), + } + } + } + + /// Adds constraints appropriate for a const expression `val` + /// in a context with ambient variance `variance` + fn add_constraints_from_const(&mut self, c: &Const, variance: Variance) { + match &c.data(Interner).value { + chalk_ir::ConstValue::Concrete(c) => { + if let ConstScalar::UnevaluatedConst(_, subst) = &c.interned { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + } + _ => {} + } + } + + /// Adds constraints appropriate for a function with signature + /// `sig` appearing in a context with ambient variance `variance` + fn add_constraints_from_sig<'a>( + &mut self, + mut sig_tys: impl DoubleEndedIterator<Item = &'a Ty>, + variance: Variance, + ) { + let contra = variance.contravariant(); + let Some(output) = sig_tys.next_back() else { + return never!("function signature has no return type"); + }; + self.add_constraints_from_ty(output, variance); + for input in sig_tys { + self.add_constraints_from_ty(input, contra); + } + } + + /// Adds constraints appropriate for a region appearing in a + /// context with ambient variance `variance` + fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { + tracing::debug!( + "add_constraints_from_region(region={:?}, variance={:?})", + region, + variance + ); + match region.data(Interner) { + LifetimeData::Placeholder(index) => { + let idx = crate::lt_from_placeholder_idx(self.db, *index); + let inferred = self.generics.lifetime_idx(idx).unwrap(); + self.constrain(inferred, variance); + } + LifetimeData::Static => {} + LifetimeData::BoundVar(..) => { + // Either a higher-ranked region inside of a type or a + // late-bound function parameter. + // + // We do not compute constraints for either of these. + } + LifetimeData::Error => {} + LifetimeData::Phantom(..) | LifetimeData::InferenceVar(..) | LifetimeData::Erased => { + // We don't expect to see anything but 'static or bound + // regions when visiting member types or method types. + never!( + "unexpected region encountered in variance \ + inference: {:?}", + region + ); + } + } + } + + /// Adds constraints appropriate for a mutability-type pair + /// appearing in a context with ambient variance `variance` + fn add_constraints_from_mt(&mut self, ty: &Ty, mt: Mutability, variance: Variance) { + self.add_constraints_from_ty( + ty, + match mt { + Mutability::Mut => variance.invariant(), + Mutability::Not => variance, + }, + ); + } + + fn constrain(&mut self, index: usize, variance: Variance) { + tracing::debug!( + "constrain(index={:?}, variance={:?}, to={:?})", + index, + self.variances[index], + variance + ); + self.variances[index] = self.variances[index].glb(variance); + } +} + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + use hir_def::{ + generics::GenericParamDataRef, src::HasSource, AdtId, GenericDefId, ModuleDefId, + }; + use itertools::Itertools; + use stdx::format_to; + use syntax::{ast::HasName, AstNode}; + use test_fixture::WithFixture; + + use hir_def::Lookup; + + use crate::{db::HirDatabase, test_db::TestDB, variance::generics}; + + #[test] + fn phantom_data() { + check( + r#" +//- minicore: phantom_data + +struct Covariant<A> { + t: core::marker::PhantomData<A> +} +"#, + expect![[r#" + Covariant[A: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_types() { + check( + r#" +//- minicore: cell + +use core::cell::UnsafeCell; + +struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR ['a: +, A: o, B: o] + t: &'a mut (A,B) +} + +struct InvariantCell<A> { //~ ERROR [A: o] + t: UnsafeCell<A> +} + +struct InvariantIndirect<A> { //~ ERROR [A: o] + t: InvariantCell<A> +} + +struct Covariant<A> { //~ ERROR [A: +] + t: A, u: fn() -> A +} + +struct Contravariant<A> { //~ ERROR [A: -] + t: fn(A) +} + +enum Enum<A,B,C> { //~ ERROR [A: +, B: -, C: o] + Foo(Covariant<A>), + Bar(Contravariant<B>),` + Zed(Covariant<C>,Contravariant<C>) +} +"#, + expect![[r#" + InvariantMut['a: covariant, A: invariant, B: invariant] + InvariantCell[A: invariant] + InvariantIndirect[A: invariant] + Covariant[A: covariant] + Contravariant[A: contravariant] + Enum[A: covariant, B: contravariant, C: invariant] + "#]], + ); + } + + #[test] + fn type_resolve_error_two_structs_deep() { + check( + r#" +struct Hello<'a> { + missing: Missing<'a>, +} + +struct Other<'a> { + hello: Hello<'a>, +} +"#, + expect![[r#" + Hello['a: bivariant] + Other['a: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_consts() { + // FIXME: Should be invariant + check( + r#" +trait Trait { + const Const: usize; +} + +struct Foo<T: Trait> { //~ ERROR [T: o] + field: [u8; <T as Trait>::Const] +} +"#, + expect![[r#" + Foo[T: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_types() { + check( + r#" +trait Trait<'a> { + type Type; + + fn method(&'a self) { } +} + +struct Foo<'a, T : Trait<'a>> { //~ ERROR ['a: +, T: +] + field: (T, &'a ()) +} + +struct Bar<'a, T : Trait<'a>> { //~ ERROR ['a: o, T: o] + field: <T as Trait<'a>>::Type +} + +"#, + expect![[r#" + method[Self: contravariant, 'a: contravariant] + Foo['a: covariant, T: covariant] + Bar['a: invariant, T: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_types2() { + // FIXME: RPITs have variance, but we can't treat them as their own thing right now + check( + r#" +trait Foo { + type Bar; +} + +fn make() -> *const dyn Foo<Bar = &'static u32> {} +"#, + expect![""], + ); + } + + #[test] + fn rustc_test_variance_trait_bounds() { + check( + r#" +trait Getter<T> { + fn get(&self) -> T; +} + +trait Setter<T> { + fn get(&self, _: T); +} + +struct TestStruct<U,T:Setter<U>> { //~ ERROR [U: +, T: +] + t: T, u: U +} + +enum TestEnum<U,T:Setter<U>> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + Foo(T) +} + +struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + t: T +} + +struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + t: T +} +"#, + expect![[r#" + get[Self: contravariant, T: covariant] + get[Self: contravariant, T: contravariant] + TestStruct[U: covariant, T: covariant] + TestEnum[U: bivariant, T: covariant] + TestContraStruct[U: bivariant, T: covariant] + TestBox[U: bivariant, T: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_trait_matching() { + check( + r#" + +trait Get<T> { + fn get(&self) -> T; +} + +struct Cloner<T:Clone> { + t: T +} + +impl<T:Clone> Get<T> for Cloner<T> { + fn get(&self) -> T {} +} + +fn get<'a, G>(get: &G) -> i32 + where G : Get<&'a i32> +{} + +fn pick<'b, G>(get: &'b G, if_odd: &'b i32) -> i32 + where G : Get<&'b i32> +{} +"#, + expect![[r#" + get[Self: contravariant, T: covariant] + Cloner[T: covariant] + get[T: invariant] + get['a: invariant, G: contravariant] + pick['b: contravariant, G: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_trait_object_bound() { + check( + r#" +enum Option<T> { + Some(T), + None +} +trait T { fn foo(&self); } + +struct TOption<'a> { //~ ERROR ['a: +] + v: Option<*const (dyn T + 'a)>, +} +"#, + expect![[r#" + Option[T: covariant] + foo[Self: contravariant] + TOption['a: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_types_bounds() { + check( + r#" +//- minicore: send +struct TestImm<A, B> { //~ ERROR [A: +, B: +] + x: A, + y: B, +} + +struct TestMut<A, B:'static> { //~ ERROR [A: +, B: o] + x: A, + y: &'static mut B, +} + +struct TestIndirect<A:'static, B:'static> { //~ ERROR [A: +, B: o] + m: TestMut<A, B> +} + +struct TestIndirect2<A:'static, B:'static> { //~ ERROR [A: o, B: o] + n: TestMut<A, B>, + m: TestMut<B, A> +} + +trait Getter<A> { + fn get(&self) -> A; +} + +trait Setter<A> { + fn set(&mut self, a: A); +} + +struct TestObject<A, R> { //~ ERROR [A: o, R: o] + n: *const (dyn Setter<A> + Send), + m: *const (dyn Getter<R> + Send), +} +"#, + expect![[r#" + TestImm[A: covariant, B: covariant] + TestMut[A: covariant, B: invariant] + TestIndirect[A: covariant, B: invariant] + TestIndirect2[A: invariant, B: invariant] + get[Self: contravariant, A: covariant] + set[Self: invariant, A: contravariant] + TestObject[A: invariant, R: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_unused_region_param() { + check( + r#" +struct SomeStruct<'a> { x: u32 } //~ ERROR parameter `'a` is never used +enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used +trait SomeTrait<'a> { fn foo(&self); } // OK on traits. +"#, + expect![[r#" + SomeStruct['a: bivariant] + SomeEnum['a: bivariant] + foo[Self: contravariant, 'a: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_unused_type_param() { + check( + r#" +//- minicore: sized +struct SomeStruct<A> { x: u32 } +enum SomeEnum<A> { Nothing } +enum ListCell<T> { + Cons(*const ListCell<T>), + Nil +} + +struct SelfTyAlias<T>(*const Self); +struct WithBounds<T: Sized> {} +struct WithWhereBounds<T> where T: Sized {} +struct WithOutlivesBounds<T: 'static> {} +struct DoubleNothing<T> { + s: SomeStruct<T>, +} + +"#, + expect![[r#" + SomeStruct[A: bivariant] + SomeEnum[A: bivariant] + ListCell[T: bivariant] + SelfTyAlias[T: bivariant] + WithBounds[T: bivariant] + WithWhereBounds[T: bivariant] + WithOutlivesBounds[T: bivariant] + DoubleNothing[T: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_contravariant_struct1() { + check( + r#" +struct SomeStruct<T>(fn(T)); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: contravariant] + foo['min: contravariant, 'max: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_contravariant_struct2() { + check( + r#" +struct SomeStruct<T>(fn(T)); + +fn bar<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: contravariant] + bar['min: covariant, 'max: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_covariant_struct1() { + check( + r#" +struct SomeStruct<T>(T); + +fn foo<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: covariant] + foo['min: contravariant, 'max: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_covariant_struct2() { + check( + r#" +struct SomeStruct<T>(T); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: covariant] + foo['min: covariant, 'max: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_invariant_struct1() { + check( + r#" +struct SomeStruct<T>(*mut T); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} + +fn bar<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: invariant] + foo['min: invariant, 'max: invariant] + bar['min: invariant, 'max: invariant] + "#]], + ); + } + + #[test] + fn invalid_arg_counts() { + check( + r#" +struct S<T>(T); +struct S2<T>(S<>); +struct S3<T>(S<T, T>); +"#, + expect![[r#" + S[T: covariant] + S2[T: bivariant] + S3[T: covariant] + "#]], + ); + } + + #[test] + fn prove_fixedpoint() { + // FIXME: This is wrong, this should be `FixedPoint[T: covariant, U: covariant, V: covariant]` + // This is a limitation of current salsa where a cycle may only set a fallback value to the + // query result, but we need to solve a fixpoint here. The new salsa will have this + // fortunately. + check( + r#" +struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V); +"#, + expect![[r#" + FixedPoint[T: bivariant, U: bivariant, V: bivariant] + "#]], + ); + } + + #[track_caller] + fn check(ra_fixture: &str, expected: Expect) { + // use tracing_subscriber::{layer::SubscriberExt, Layer}; + // let my_layer = tracing_subscriber::fmt::layer(); + // let _g = tracing::subscriber::set_default(tracing_subscriber::registry().with( + // my_layer.with_filter(tracing_subscriber::filter::filter_fn(|metadata| { + // metadata.target().starts_with("hir_ty::variance") + // })), + // )); + let (db, file_id) = TestDB::with_single_file(ra_fixture); + + let mut defs: Vec<GenericDefId> = Vec::new(); + let module = db.module_for_file_opt(file_id).unwrap(); + let def_map = module.def_map(&db); + crate::tests::visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::AdtId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::TraitId(it) => it.into(), + ModuleDefId::TraitAliasId(it) => it.into(), + ModuleDefId::TypeAliasId(it) => it.into(), + _ => return, + }) + }); + let defs = defs + .into_iter() + .filter_map(|def| { + Some(( + def, + match def { + GenericDefId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::EnumId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::StructId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::UnionId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitAliasId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TypeAliasId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::ImplId(_) => return None, + GenericDefId::ConstId(_) => return None, + }, + )) + }) + .sorted_by_key(|(_, n)| n.syntax().text_range().start()); + let mut res = String::new(); + for (def, name) in defs { + let Some(variances) = db.variances_of(def) else { + continue; + }; + format_to!( + res, + "{name}[{}]\n", + generics(&db, def) + .iter() + .map(|(_, param)| match param { + GenericParamDataRef::TypeParamData(type_param_data) => { + type_param_data.name.as_ref().unwrap() + } + GenericParamDataRef::ConstParamData(const_param_data) => + &const_param_data.name, + GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { + &lifetime_param_data.name + } + }) + .zip_eq(&*variances) + .format_with(", ", |(name, var), f| f(&format_args!( + "{}: {var}", + name.as_str() + ))) + ); + } + + expected.assert_eq(&res); + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index af60c233e55..a23fdf1b393 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -258,7 +258,7 @@ fn resolve_impl_trait_item( &traits_in_scope, method_resolution::VisibleFromModule::None, Some(name), - &mut |assoc_item_id| { + &mut |_, assoc_item_id: AssocItemId, _| { // If two traits in scope define the same item, Rustdoc links to no specific trait (for // instance, given two methods `a`, Rustdoc simply links to `method.a` with no // disambiguation) so we just pick the first one we find as well. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index cbb1ed95ed6..fc77d1889c8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,12 +15,12 @@ use hir_expand::{name::Name, HirFileId, InFile}; use hir_ty::{ db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, + CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic, + TyLoweringDiagnostic, TyLoweringDiagnosticKind, }; use syntax::{ ast::{self, HasGenericArgs}, - AstPtr, SyntaxError, SyntaxNodePtr, TextRange, + match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, }; use triomphe::Arc; @@ -674,6 +674,39 @@ impl AnyDiagnostic { }; Self::ty_diagnostic(diag, source_map, db)? } + InferenceDiagnostic::PathDiagnostic { node, diag } => { + let source = expr_or_pat_syntax(*node)?; + let syntax = source.value.to_node(&db.parse_or_expand(source.file_id)); + let path = match_ast! { + match (syntax.syntax()) { + ast::RecordExpr(it) => it.path()?, + ast::RecordPat(it) => it.path()?, + ast::TupleStructPat(it) => it.path()?, + ast::PathExpr(it) => it.path()?, + ast::PathPat(it) => it.path()?, + _ => return None, + } + }; + Self::path_diagnostic(diag, source.with_value(path))? + } + }) + } + + fn path_diagnostic( + diag: &PathLoweringDiagnostic, + path: InFile<ast::Path>, + ) -> Option<AnyDiagnostic> { + Some(match diag { + &PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => { + let segment = hir_segment_to_ast_segment(&path.value, segment)?; + let args = if let Some(generics) = segment.generic_arg_list() { + AstPtr::new(&generics).wrap_left() + } else { + AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right() + }; + let args = path.with_value(args); + GenericArgsProhibited { args, reason }.into() + } }) } @@ -693,17 +726,10 @@ impl AnyDiagnostic { Either::Right(source) => source, }; let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); - Some(match diag.kind { - TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => { + Some(match &diag.kind { + TyLoweringDiagnosticKind::PathDiagnostic(diag) => { let ast::Type::PathType(syntax) = syntax() else { return None }; - let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?; - let args = if let Some(generics) = segment.generic_arg_list() { - AstPtr::new(&generics).wrap_left() - } else { - AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right() - }; - let args = source.with_value(args); - GenericArgsProhibited { args, reason }.into() + Self::path_diagnostic(diag, source.with_value(syntax.path()?))? } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 959d62d5951..e09ded32fbd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -1,7 +1,10 @@ //! HirDisplay implementations for various hir types. use either::Either; use hir_def::{ - data::adt::{StructKind, VariantData}, + data::{ + adt::{StructKind, VariantData}, + TraitFlags, + }, generics::{ GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, @@ -22,7 +25,7 @@ use itertools::Itertools; use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, - SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias, + SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; @@ -743,6 +746,12 @@ impl HirDisplay for Static { } } +impl HirDisplay for TraitRef { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + self.trait_ref.hir_fmt(f) + } +} + impl HirDisplay for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_trait_header(self, f)?; @@ -785,10 +794,10 @@ impl HirDisplay for Trait { fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?; let data = f.db.trait_data(trait_.id); - if data.is_unsafe { + if data.flags.contains(TraitFlags::IS_UNSAFE) { f.write_str("unsafe ")?; } - if data.is_auto { + if data.flags.contains(TraitFlags::IS_AUTO) { f.write_str("auto ")?; } write!(f, "trait {}", data.name.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index dfc91c73433..00b4db54374 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use base_db::{CrateDisplayName, CrateId, CrateOrigin}; use either::Either; use hir_def::{ body::BodyDiagnostic, - data::adt::VariantData, + data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, @@ -101,7 +101,6 @@ pub use crate::{ PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits, }, }; -pub use hir_ty::method_resolution::TyFingerprint; // Be careful with these re-exports. // @@ -151,8 +150,9 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode}, layout::LayoutError, + method_resolution::TyFingerprint, mir::{MirEvalError, MirLowerError}, - CastError, FnAbi, PointerCast, Safety, + CastError, FnAbi, PointerCast, Safety, Variance, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -699,7 +699,7 @@ impl Module { let source_map = tree_source_maps.impl_(loc.id.value).item(); let node = &tree[loc.id.value]; let file_id = loc.id.file_id(); - if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) { + if file_id.macro_file().is_some_and(|it| it.is_builtin_derive(db.upcast())) { // these expansion come from us, diagnosing them is a waste of resources // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; @@ -724,7 +724,7 @@ impl Module { } let trait_ = impl_def.trait_(db); - let trait_is_unsafe = trait_.map_or(false, |t| t.is_unsafe(db)); + let trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); let impl_is_negative = impl_def.is_negative(db); let impl_is_unsafe = impl_def.is_unsafe(db); @@ -2778,11 +2778,11 @@ impl Trait { } pub fn is_auto(self, db: &dyn HirDatabase) -> bool { - db.trait_data(self.id).is_auto + db.trait_data(self.id).flags.contains(TraitFlags::IS_AUTO) } pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool { - db.trait_data(self.id).is_unsafe + db.trait_data(self.id).flags.contains(TraitFlags::IS_UNSAFE) } pub fn type_or_const_param_count( @@ -3574,6 +3574,61 @@ impl GenericDef { } } +// We cannot call this `Substitution` unfortunately... +#[derive(Debug)] +pub struct GenericSubstitution { + def: GenericDefId, + subst: Substitution, + env: Arc<TraitEnvironment>, +} + +impl GenericSubstitution { + fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment>) -> Self { + Self { def, subst, env } + } + + pub fn types(&self, db: &dyn HirDatabase) -> Vec<(Symbol, Type)> { + let container = match self.def { + GenericDefId::ConstId(id) => Some(id.lookup(db.upcast()).container), + GenericDefId::FunctionId(id) => Some(id.lookup(db.upcast()).container), + GenericDefId::TypeAliasId(id) => Some(id.lookup(db.upcast()).container), + _ => None, + }; + let container_type_params = container + .and_then(|container| match container { + ItemContainerId::ImplId(container) => Some(container.into()), + ItemContainerId::TraitId(container) => Some(container.into()), + _ => None, + }) + .map(|container| { + db.generic_params(container) + .iter_type_or_consts() + .filter_map(|param| match param.1 { + TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), + TypeOrConstParamData::ConstParamData(_) => None, + }) + .collect::<Vec<_>>() + }); + let generics = db.generic_params(self.def); + let type_params = generics.iter_type_or_consts().filter_map(|param| match param.1 { + TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), + TypeOrConstParamData::ConstParamData(_) => None, + }); + // The `Substitution` is first self then container, we want the reverse order. + let self_params = self.subst.type_parameters(Interner).zip(type_params); + let container_params = self.subst.as_slice(Interner)[generics.len()..] + .iter() + .filter_map(|param| param.ty(Interner).cloned()) + .zip(container_type_params.into_iter().flatten()); + container_params + .chain(self_params) + .filter_map(|(ty, name)| { + Some((name?.symbol().clone(), Type { ty, env: self.env.clone() })) + }) + .collect() + } +} + /// A single local definition. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Local { @@ -3902,6 +3957,22 @@ impl GenericParam { GenericParam::LifetimeParam(it) => it.id.parent.into(), } } + + pub fn variance(self, db: &dyn HirDatabase) -> Option<Variance> { + let parent = match self { + GenericParam::TypeParam(it) => it.id.parent(), + // const parameters are always invariant + GenericParam::ConstParam(_) => return None, + GenericParam::LifetimeParam(it) => it.id.parent, + }; + let generics = hir_ty::generics::generics(db.upcast(), parent); + let index = match self { + GenericParam::TypeParam(it) => generics.type_or_const_param_idx(it.id.into())?, + GenericParam::ConstParam(_) => return None, + GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?, + }; + db.variances_of(parent)?.get(index).copied() + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -5152,21 +5223,18 @@ impl Type { ) -> Option<T> { let _p = tracing::info_span!("iterate_method_candidates_with_traits").entered(); let mut slot = None; - - self.iterate_method_candidates_dyn( + self.iterate_method_candidates_split_inherent( db, scope, traits_in_scope, with_local_impls, name, - &mut |assoc_item_id| { - if let AssocItemId::FunctionId(func) = assoc_item_id { - if let Some(res) = callback(func.into()) { - slot = Some(res); - return ControlFlow::Break(()); - } + |f| match callback(f) { + it @ Some(_) => { + slot = it; + ControlFlow::Break(()) } - ControlFlow::Continue(()) + None => ControlFlow::Continue(()), }, ); slot @@ -5190,15 +5258,49 @@ impl Type { ) } - fn iterate_method_candidates_dyn( + /// Allows you to treat inherent and non-inherent methods differently. + /// + /// Note that inherent methods may actually be trait methods! For example, in `dyn Trait`, the trait's methods + /// are considered inherent methods. + pub fn iterate_method_candidates_split_inherent( &self, db: &dyn HirDatabase, scope: &SemanticsScope<'_>, traits_in_scope: &FxHashSet<TraitId>, with_local_impls: Option<Module>, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: impl MethodCandidateCallback, ) { + struct Callback<T>(T); + + impl<T: MethodCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> { + fn on_inherent_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + if let AssocItemId::FunctionId(func) = item { + self.0.on_inherent_method(func.into()) + } else { + ControlFlow::Continue(()) + } + } + + fn on_trait_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + if let AssocItemId::FunctionId(func) = item { + self.0.on_trait_method(func.into()) + } else { + ControlFlow::Continue(()) + } + } + } + let _p = tracing::info_span!( "iterate_method_candidates_dyn", with_local_impls = traits_in_scope.len(), @@ -5223,7 +5325,7 @@ impl Type { with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::MethodCall, - &mut |_adj, id, _| callback(id), + &mut Callback(callback), ); } @@ -5239,33 +5341,61 @@ impl Type { ) -> Option<T> { let _p = tracing::info_span!("iterate_path_candidates").entered(); let mut slot = None; - self.iterate_path_candidates_dyn( + + self.iterate_path_candidates_split_inherent( db, scope, traits_in_scope, with_local_impls, name, - &mut |assoc_item_id| { - if let Some(res) = callback(assoc_item_id.into()) { - slot = Some(res); - return ControlFlow::Break(()); + |item| match callback(item) { + it @ Some(_) => { + slot = it; + ControlFlow::Break(()) } - ControlFlow::Continue(()) + None => ControlFlow::Continue(()), }, ); slot } + /// Iterates over inherent methods. + /// + /// In some circumstances, inherent methods methods may actually be trait methods! + /// For example, when `dyn Trait` is a receiver, _trait_'s methods would be considered + /// to be inherent methods. #[tracing::instrument(skip_all, fields(name = ?name))] - fn iterate_path_candidates_dyn( + pub fn iterate_path_candidates_split_inherent( &self, db: &dyn HirDatabase, scope: &SemanticsScope<'_>, traits_in_scope: &FxHashSet<TraitId>, with_local_impls: Option<Module>, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: impl PathCandidateCallback, ) { + struct Callback<T>(T); + + impl<T: PathCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> { + fn on_inherent_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + self.0.on_inherent_item(item.into()) + } + + fn on_trait_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + self.0.on_trait_item(item.into()) + } + } + let canonical = hir_ty::replace_errors_with_variables(&self.ty); let krate = scope.krate(); @@ -5281,7 +5411,7 @@ impl Type { traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, - callback, + &mut Callback(callback), ); } @@ -5862,6 +5992,12 @@ impl HasCrate for Adt { } } +impl HasCrate for Impl { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Module { fn krate(&self, _: &dyn HirDatabase) -> Crate { Module::krate(*self) @@ -5983,3 +6119,41 @@ fn push_ty_diagnostics( ); } } + +pub trait MethodCandidateCallback { + fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()>; + + fn on_trait_method(&mut self, f: Function) -> ControlFlow<()>; +} + +impl<F> MethodCandidateCallback for F +where + F: FnMut(Function) -> ControlFlow<()>, +{ + fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()> { + self(f) + } + + fn on_trait_method(&mut self, f: Function) -> ControlFlow<()> { + self(f) + } +} + +pub trait PathCandidateCallback { + fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()>; + + fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()>; +} + +impl<F> PathCandidateCallback for F +where + F: FnMut(AssocItem) -> ControlFlow<()>, +{ + fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()> { + self(item) + } + + fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()> { + self(item) + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 1cf22b05e7f..34d169cd761 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -30,7 +30,7 @@ use hir_expand::{ name::AsName, ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt, }; -use intern::Symbol; +use intern::{sym, Symbol}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; @@ -49,10 +49,10 @@ use crate::{ semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, - ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, - OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, + HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, + Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, + TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(()); @@ -246,59 +246,59 @@ impl<DB: HirDatabase> Semantics<'_, DB> { } pub fn to_adt_def(&self, a: &ast::Adt) -> Option<Adt> { - self.imp.to_def(a).map(Adt::from) + self.imp.to_def(a) } pub fn to_const_def(&self, c: &ast::Const) -> Option<Const> { - self.imp.to_def(c).map(Const::from) + self.imp.to_def(c) } pub fn to_enum_def(&self, e: &ast::Enum) -> Option<Enum> { - self.imp.to_def(e).map(Enum::from) + self.imp.to_def(e) } pub fn to_enum_variant_def(&self, v: &ast::Variant) -> Option<Variant> { - self.imp.to_def(v).map(Variant::from) + self.imp.to_def(v) } pub fn to_fn_def(&self, f: &ast::Fn) -> Option<Function> { - self.imp.to_def(f).map(Function::from) + self.imp.to_def(f) } pub fn to_impl_def(&self, i: &ast::Impl) -> Option<Impl> { - self.imp.to_def(i).map(Impl::from) + self.imp.to_def(i) } pub fn to_macro_def(&self, m: &ast::Macro) -> Option<Macro> { - self.imp.to_def(m).map(Macro::from) + self.imp.to_def(m) } pub fn to_module_def(&self, m: &ast::Module) -> Option<Module> { - self.imp.to_def(m).map(Module::from) + self.imp.to_def(m) } pub fn to_static_def(&self, s: &ast::Static) -> Option<Static> { - self.imp.to_def(s).map(Static::from) + self.imp.to_def(s) } pub fn to_struct_def(&self, s: &ast::Struct) -> Option<Struct> { - self.imp.to_def(s).map(Struct::from) + self.imp.to_def(s) } pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option<TraitAlias> { - self.imp.to_def(t).map(TraitAlias::from) + self.imp.to_def(t) } pub fn to_trait_def(&self, t: &ast::Trait) -> Option<Trait> { - self.imp.to_def(t).map(Trait::from) + self.imp.to_def(t) } pub fn to_type_alias_def(&self, t: &ast::TypeAlias) -> Option<TypeAlias> { - self.imp.to_def(t).map(TypeAlias::from) + self.imp.to_def(t) } pub fn to_union_def(&self, u: &ast::Union) -> Option<Union> { - self.imp.to_def(u).map(Union::from) + self.imp.to_def(u) } } @@ -811,10 +811,37 @@ impl<'db> SemanticsImpl<'db> { item.attrs().any(|attr| { let Some(meta) = attr.meta() else { return false }; let Some(path) = meta.path() else { return false }; - let Some(attr_name) = path.as_single_name_ref() else { return true }; - let attr_name = attr_name.text(); - let attr_name = attr_name.as_str(); - attr_name == "derive" || find_builtin_attr_idx(&Symbol::intern(attr_name)).is_none() + if let Some(attr_name) = path.as_single_name_ref() { + let attr_name = attr_name.text(); + let attr_name = Symbol::intern(attr_name.as_str()); + if attr_name == sym::derive { + return true; + } + // We ignore `#[test]` and friends in the def map, so we cannot expand them. + // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create + // other macros named `test`). We cannot fix that unfortunately because we use this method + // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros + // named `test` are test-like, meaning their expansion is not terribly important for IDE. + if attr_name == sym::test + || attr_name == sym::bench + || attr_name == sym::test_case + || find_builtin_attr_idx(&attr_name).is_some() + { + return false; + } + } + let mut segments = path.segments(); + let mut next_segment_text = || segments.next().and_then(|it| it.name_ref()); + // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`. + if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std")) + && next_segment_text().is_some_and(|it| it.text() == "prelude") + && next_segment_text().is_some() + && next_segment_text() + .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case")) + { + return false; + } + true }) }) } @@ -1206,7 +1233,6 @@ impl<'db> SemanticsImpl<'db> { node.original_file_range_opt(self.db.upcast()) .filter(|(_, ctx)| ctx.is_root()) .map(TupleExt::head) - .map(Into::into) } /// Attempts to map the node out of macro expanded files. @@ -1413,7 +1439,7 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_method_call_fallback( &self, call: &ast::MethodCallExpr, - ) -> Option<Either<Function, Field>> { + ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } @@ -1456,7 +1482,7 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_field_fallback( &self, field: &ast::FieldExpr, - ) -> Option<Either<Either<Field, TupleField>, Function>> { + ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> { self.analyze(field.syntax())?.resolve_field_fallback(self.db, field) } @@ -1464,10 +1490,25 @@ impl<'db> SemanticsImpl<'db> { &self, field: &ast::RecordExprField, ) -> Option<(Field, Option<Local>, Type)> { + self.resolve_record_field_with_substitution(field) + .map(|(field, local, ty, _)| (field, local, ty)) + } + + pub fn resolve_record_field_with_substitution( + &self, + field: &ast::RecordExprField, + ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> { self.analyze(field.syntax())?.resolve_record_field(self.db, field) } pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { + self.resolve_record_pat_field_with_subst(field).map(|(field, ty, _)| (field, ty)) + } + + pub fn resolve_record_pat_field_with_subst( + &self, + field: &ast::RecordPatField, + ) -> Option<(Field, Type, GenericSubstitution)> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } @@ -1485,7 +1526,7 @@ impl<'db> SemanticsImpl<'db> { pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool { self.resolve_macro_call(macro_call) - .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) + .is_some_and(|m| matches!(m.id, MacroId::ProcMacroId(..))) } pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> { @@ -1523,6 +1564,13 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { + self.resolve_path_with_subst(path).map(|(it, _)| it) + } + + pub fn resolve_path_with_subst( + &self, + path: &ast::Path, + ) -> Option<(PathResolution, Option<GenericSubstitution>)> { self.analyze(path.syntax())?.resolve_path(self.db, path) } @@ -2038,6 +2086,11 @@ impl SemanticsScope<'_> { ) } + pub fn resolve_mod_path(&self, path: &ModPath) -> impl Iterator<Item = ItemInNs> { + let items = self.resolver.resolve_module_path_in_items(self.db.upcast(), path); + items.iter_items().map(|(item, _)| item.into()) + } + /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). pub fn assoc_type_shorthand_candidates<R>( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 08333c2d76c..b5cc440fc22 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -408,7 +408,7 @@ impl SourceToDefCtx<'_, '_> { } pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool { - self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty()) + self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty()) } fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 4329a888b39..b699ccde412 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -9,8 +9,8 @@ use std::iter::{self, once}; use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, - Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, + BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, + ModuleDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, }; use either::Either; use hir_def::{ @@ -18,15 +18,15 @@ use hir_def::{ scope::{ExprScopes, ScopeId}, Body, BodySourceMap, HygieneId, }, - hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId}, lang_item::LangItem, lower::LowerCtx, nameres::MacroSubNs, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::{Mutability, TypesMap, TypesSourceMap}, - AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, - LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, + AsMacroCall, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, + ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, }; use hir_expand::{ mod_path::path, @@ -38,9 +38,10 @@ use hir_ty::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, InsideUnsafeBlock, }, + from_assoc_type_id, lang_items::lang_items_for_bin_op, - method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, - TyLoweringContext, + method_resolution, Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, + TyExt, TyKind, TyLoweringContext, }; use intern::sym; use itertools::Itertools; @@ -120,6 +121,13 @@ impl SourceAnalyzer { self.def.as_ref().map(|(_, body, _)| &**body) } + fn trait_environment(&self, db: &dyn HirDatabase) -> Arc<TraitEnvironment> { + self.def.as_ref().map(|(def, ..)| *def).map_or_else( + || TraitEnvironment::empty(self.resolver.krate()), + |def| db.trait_environment_for_body(def), + ) + } + fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> { let src = match expr { ast::Expr::MacroExpr(expr) => { @@ -294,18 +302,23 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, call: &ast::MethodCallExpr, - ) -> Option<Either<Function, Field>> { + ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> { let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.method_resolution(expr_id) { - Some((f_in_trait, substs)) => Some(Either::Left( - self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(), - )), - None => inference_result - .field_resolution(expr_id) - .and_then(Either::left) - .map(Into::into) - .map(Either::Right), + Some((f_in_trait, substs)) => { + let (fn_, subst) = + self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); + Some(( + Either::Left(fn_.into()), + Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))), + )) + } + None => { + inference_result.field_resolution(expr_id).and_then(Either::left).map(|field| { + (Either::Right(field.into()), self.field_subst(expr_id, inference_result, db)) + }) + } } } @@ -330,22 +343,53 @@ impl SourceAnalyzer { }) } + fn field_subst( + &self, + field_expr: ExprId, + infer: &InferenceResult, + db: &dyn HirDatabase, + ) -> Option<GenericSubstitution> { + let body = self.body()?; + if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { + let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?; + return Some(GenericSubstitution::new( + adt.into(), + subst.clone(), + self.trait_environment(db), + )); + } + None + } + pub(crate) fn resolve_field_fallback( &self, db: &dyn HirDatabase, field: &ast::FieldExpr, - ) -> Option<Either<Either<Field, TupleField>, Function>> { + ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> { let &(def, ..) = self.def.as_ref()?; let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.field_resolution(expr_id) { - Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField { - owner: def, - tuple: f.tuple, - index: f.index, - }))), + Some(field) => match field { + Either::Left(field) => Some(( + Either::Left(Either::Left(field.into())), + self.field_subst(expr_id, inference_result, db), + )), + Either::Right(field) => Some(( + Either::Left(Either::Right(TupleField { + owner: def, + tuple: field.tuple, + index: field.index, + })), + None, + )), + }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { - Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into()) + let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); + ( + Either::Right(f.into()), + Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))), + ) }), } } @@ -557,7 +601,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, field: &ast::RecordExprField, - ) -> Option<(Field, Option<Local>, Type)> { + ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let expr = ast::Expr::from(record_expr); let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?; @@ -583,30 +627,39 @@ impl SourceAnalyzer { _ => None, } }; - let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + local, + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_record_pat_field( &self, db: &dyn HirDatabase, field: &ast::RecordPatField, - ) -> Option<(Field, Type)> { + ) -> Option<(Field, Type, GenericSubstitution)> { let field_name = field.field_name()?.as_name(); let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; - let (_, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_macro_call( @@ -654,7 +707,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, path: &ast::Path, - ) -> Option<PathResolution> { + ) -> Option<(PathResolution, Option<GenericSubstitution>)> { let parent = path.syntax().parent(); let parent = || parent.clone(); @@ -664,60 +717,106 @@ impl SourceAnalyzer { if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { let expr_id = self.expr_id(db, &path_expr.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::FunctionId(f_in_trait) => { match infer.type_of_expr_or_pat(expr_id) { - None => assoc, + None => { + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) + } Some(func_ty) => { if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) { - self.resolve_impl_method_or_trait_def( - db, - f_in_trait, - subs.clone(), - ) - .into() + let (fn_, subst) = self + .resolve_impl_method_or_trait_def_with_subst( + db, + f_in_trait, + subs.clone(), + ); + let subst = GenericSubstitution::new( + fn_.into(), + subst, + self.trait_environment(db), + ); + (fn_.into(), subst) } else { - assoc + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) } } } } AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + AssocItemId::TypeAliasId(type_alias) => ( + assoc, + GenericSubstitution::new( + type_alias.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } prefer_value_ns = true; } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) { let pat_id = self.pat_id(&path_pat.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + assoc => ( + assoc, + GenericSubstitution::new( + assoc.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_pat(pat_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { let expr_id = self.expr_id(db, &rec_lit.into())?; if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else { let record_pat = parent().and_then(ast::RecordPat::cast).map(ast::Pat::from); @@ -727,7 +826,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&pat)?; let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id); if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some(( + PathResolution::Def(ModuleDef::Variant(variant.into())), + None, + )); } } } @@ -747,7 +849,8 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { if use_tree.coloncolon_token().is_some() { - return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map); + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)); } } @@ -765,13 +868,18 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if path.parent_path().is_some() { return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) { - None if meta_path.is_some() => { - path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { + None if meta_path.is_some() => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) .map(PathResolution::ToolModule) }) - } - res => res, + .map(|it| (it, None)), + // FIXME: We do not show substitutions for parts of path, because this is really complex + // due to the interactions with associated items of `impl`s and associated items of associated + // types. + res => res.map(|it| (it, None)), }; } else if let Some(meta_path) = meta_path { // Case where we are resolving the final path segment of a path in an attribute @@ -781,7 +889,7 @@ impl SourceAnalyzer { let builtin = BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); if builtin.is_some() { - return builtin.map(PathResolution::BuiltinAttr); + return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } if let Some(attr) = meta_path.parent_attr() { @@ -814,10 +922,13 @@ impl SourceAnalyzer { { if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref) { - return Some(PathResolution::DeriveHelper(DeriveHelper { - derive: *macro_id, - idx: idx as u32, - })); + return Some(( + PathResolution::DeriveHelper(DeriveHelper { + derive: *macro_id, + idx: idx as u32, + }), + None, + )); } } } @@ -825,26 +936,79 @@ impl SourceAnalyzer { } } return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) { - Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), + Some(m) => Some((PathResolution::Def(ModuleDef::Macro(m)), None)), // this labels any path that starts with a tool module as the tool itself, this is technically wrong // but there is no benefit in differentiating these two cases for the time being - None => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) - .map(PathResolution::ToolModule) - }), + None => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { + ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + .map(PathResolution::ToolModule) + }) + .map(|it| (it, None)), }; } - if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { + if parent().is_some_and(|it| ast::Visibility::can_cast(it.kind())) { + // No substitution because only modules can be inside visibilities, and those have no generics. resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)) } else { - resolve_hir_path_( + // Probably a type, no need to show substitutions for those. + let res = resolve_hir_path_( db, &self.resolver, &hir_path, prefer_value_ns, name_hygiene(db, InFile::new(self.file_id, path.syntax())), &types_map, - ) + )?; + let subst = (|| { + let parent = parent()?; + let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { + let expr_id = self.expr_id(db, &expr)?; + self.infer.as_ref()?.type_of_expr_or_pat(expr_id)? + } else if let Some(pat) = ast::Pat::cast(parent) { + let pat_id = self.pat_id(&pat)?; + &self.infer.as_ref()?[pat_id] + } else { + return None; + }; + let env = self.trait_environment(db); + let (subst, expected_resolution) = match ty.kind(Interner) { + TyKind::Adt(adt_id, subst) => ( + GenericSubstitution::new(adt_id.0.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::Adt(adt_id.0.into())), + ), + TyKind::AssociatedType(assoc_id, subst) => { + let assoc_id = from_assoc_type_id(*assoc_id); + ( + GenericSubstitution::new(assoc_id.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), + ) + } + TyKind::FnDef(fn_id, subst) => { + let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id); + let fn_id = db.lookup_intern_callable_def(fn_id); + let generic_def_id = match fn_id { + CallableDefId::StructId(id) => id.into(), + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::EnumVariantId(_) => return None, + }; + ( + GenericSubstitution::new(generic_def_id, subst.clone(), env), + PathResolution::Def(ModuleDefId::from(fn_id).into()), + ) + } + _ => return None, + }; + if res != expected_resolution { + // The user will not understand where we're coming from. This can happen (I think) with type aliases. + return None; + } + Some(subst) + })(); + Some((res, subst)) } } @@ -1041,26 +1205,35 @@ impl SourceAnalyzer { func: FunctionId, substs: Substitution, ) -> FunctionId { + self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0 + } + + fn resolve_impl_method_or_trait_def_with_subst( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: Substitution, + ) -> (FunctionId, Substitution) { let owner = match self.resolver.body_owner() { Some(it) => it, - None => return func, + None => return (func, substs), }; let env = db.trait_environment_for_body(owner); - db.lookup_impl_method(env, func, substs).0 + db.lookup_impl_method(env, func, substs) } - fn resolve_impl_const_or_trait_def( + fn resolve_impl_const_or_trait_def_with_subst( &self, db: &dyn HirDatabase, const_id: ConstId, subs: Substitution, - ) -> ConstId { + ) -> (ConstId, Substitution) { let owner = match self.resolver.body_owner() { Some(it) => it, - None => return const_id, + None => return (const_id, subs), }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_const(db, env, const_id, subs).0 + method_resolution::lookup_impl_const(db, env, const_id, subs) } fn lang_trait_fn( @@ -1413,3 +1586,10 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let ctx = db.lookup_intern_syntax_context(ctx); HygieneId::new(ctx.opaque_and_semitransparent) } + +fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> { + match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { + Some(adjustment) => Some(&adjustment.target), + None => infer.type_of_expr.get(id), + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 12213c8455c..43c0a72fa47 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs @@ -28,7 +28,7 @@ pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) - let node = ctx.find_node_at_offset::<ast::Adt>()?; let has_lifetime = node .generic_param_list() - .map_or(false, |gen_list| gen_list.lifetime_params().next().is_some()); + .is_some_and(|gen_list| gen_list.lifetime_params().next().is_some()); if has_lifetime { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 236d33878ed..24b34f140bd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -220,7 +220,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .arms() .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); if let Some(arm) = catch_all_arm { - let is_empty_expr = arm.expr().map_or(true, |e| match e { + let is_empty_expr = arm.expr().is_none_or(|e| match e { ast::Expr::BlockExpr(b) => { b.statements().next().is_none() && b.tail_expr().is_none() } @@ -261,7 +261,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } if let Some(cap) = ctx.config.snippet_cap { - if let Some(it) = first_new_arm.and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast)) { + if let Some(it) = first_new_arm + .and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast)) + { edit.add_placeholder_snippet(cap, it); } @@ -287,14 +289,10 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) syntax::SyntaxElement::from(edit.make_syntax_mut(it)) } syntax::SyntaxElement::Token(it) => { - // Don't have a way to make tokens mut, so instead make the parent mut - // and find the token again - let parent = - edit.make_syntax_mut(it.parent().expect("Token must have a parent.")); - let mut_token = - parent.covering_element(it.text_range()).into_token().expect("Covering element cannot be found. Range may be beyond the current node's range"); - - syntax::SyntaxElement::from(mut_token) + // If a token is found, it is '{' or '}' + // The parent is `{ ... }` + let parent = it.parent().expect("Token must have a parent."); + syntax::SyntaxElement::from(edit.make_syntax_mut(parent)) } } }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 0f6970d9403..62700ab1809 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -69,7 +69,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let ident = name_ref.ident_token()?; let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { - NameRefClass::Definition(def) => def, + NameRefClass::Definition(def, _) => def, NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => { return None } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index c7f41ffce04..fd159eb824d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -103,7 +103,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti ast::Expr::PathExpr(path) => { let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; match NameRefClass::classify(&ctx.sema, &name_ref)? { - NameRefClass::Definition(Definition::Local(local)) => { + NameRefClass::Definition(Definition::Local(local), _) => { let source = local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name()); source.collect() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index c30f3e1c3b2..ea2752b8818 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -63,7 +63,7 @@ pub(crate) fn convert_nested_function_to_closure( /// Returns whether the given function is nested within the body of another function. fn is_nested_function(function: &ast::Fn) -> bool { - function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| { + function.syntax().ancestors().skip(1).find_map(ast::Item::cast).is_some_and(|it| { matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_)) }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 83f4a6b123c..3c84f83906a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -163,8 +163,8 @@ fn edit_struct_references( // this also includes method calls like Foo::new(42), we should skip them if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) { match NameRefClass::classify(&ctx.sema, &name_ref) { - Some(NameRefClass::Definition(Definition::SelfType(_))) => {}, - Some(NameRefClass::Definition(def)) if def == strukt_def => {}, + Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {}, + Some(NameRefClass::Definition(def, _)) if def == strukt_def => {}, _ => return None, }; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 22a1efdbea7..e34e5090487 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -96,8 +96,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str let struct_def_path = module.find_path(ctx.db(), struct_def, cfg)?; let is_non_exhaustive = struct_def.attrs(ctx.db())?.by_key(&sym::non_exhaustive).exists(); - let is_foreign_crate = - struct_def.module(ctx.db()).map_or(false, |m| m.krate() != module.krate()); + let is_foreign_crate = struct_def.module(ctx.db()).is_some_and(|m| m.krate() != module.krate()); let fields = struct_type.fields(ctx.db()); let n_fields = fields.len(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index b9142d0318a..7df6ca1565f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -6,7 +6,6 @@ use ide_db::{ text_edit::TextRange, }; use itertools::Itertools; -use syntax::SmolStr; use syntax::{ ast::{self, make, AstNode, FieldExpr, HasName, IdentPat}, ted, @@ -134,17 +133,8 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat .map(|(_, refs)| refs.to_vec()) }); - let mut name_generator = { - let mut names = vec![]; - if let Some(scope) = ctx.sema.scope(ident_pat.syntax()) { - scope.process_all_names(&mut |name, scope| { - if let hir::ScopeDef::Local(_) = scope { - names.push(name.as_str().into()) - } - }) - } - suggest_name::NameGenerator::new_with_names(names.iter().map(|s: &SmolStr| s.as_str())) - }; + let mut name_generator = + suggest_name::NameGenerator::new_from_scope_locals(ctx.sema.scope(ident_pat.syntax())); let field_names = field_types .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 3d6d37ad93d..094fdc46eb7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -275,6 +275,7 @@ fn find_imported_defs(ctx: &AssistContext<'_>, star: SyntaxToken) -> Option<Vec< | Definition::Static(_) | Definition::Trait(_) | Definition::TypeAlias(_)), + _, ) => Some(def), _ => None, }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 438769a0a87..0d1b6af7204 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -800,8 +800,8 @@ impl FunctionBody { let local_ref = match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) { Some( - NameRefClass::Definition(Definition::Local(local_ref)) - | NameRefClass::FieldShorthand { local_ref, field_ref: _ }, + NameRefClass::Definition(Definition::Local(local_ref), _) + | NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ }, ) => local_ref, _ => return, }; @@ -856,7 +856,7 @@ impl FunctionBody { let mut set_parent_loop = |loop_: &dyn ast::HasLoopBody| { if loop_ .loop_body() - .map_or(false, |it| it.syntax().text_range().contains_range(self.text_range())) + .is_some_and(|it| it.syntax().text_range().contains_range(self.text_range())) { parent_loop.get_or_insert(loop_.syntax().clone()); } @@ -1090,7 +1090,7 @@ impl FunctionBody { let defined_outside_parent_loop = container_info .parent_loop .as_ref() - .map_or(true, |it| it.text_range().contains_range(src.syntax().text_range())); + .is_none_or(|it| it.text_range().contains_range(src.syntax().text_range())); let is_copy = ty.is_copy(ctx.db()); let has_usages = self.has_usages_after_body(&usages); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index e4cba666af7..6e3be0ce692 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -425,7 +425,9 @@ impl Module { }) } else if let Some(name_ref) = ast::NameRef::cast(x) { NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc { - NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)), + NameRefClass::Definition(def, _) => { + Some((name_ref.syntax().clone(), def)) + } _ => None, }) } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 1eaf31628f3..67b8f5e5050 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -100,7 +100,7 @@ fn collect_used_generics<'gp>( fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ { move |gp: &&ast::GenericParam| match gp { ast::GenericParam::LifetimeParam(lp) => { - lp.lifetime().map_or(false, |lt| lt.text() == text) + lp.lifetime().is_some_and(|lt| lt.text() == text) } _ => false, } @@ -118,7 +118,7 @@ fn collect_used_generics<'gp>( ast::GenericParam::TypeParam(tp) => tp.name(), _ => None, } - .map_or(false, |n| n.text() == name_ref.text()) + .is_some_and(|n| n.text() == name_ref.text()) }) { generics.push(param); } @@ -165,7 +165,7 @@ fn collect_used_generics<'gp>( if let Some(name_ref) = path.as_single_name_ref() { if let Some(param) = known_generics.iter().find(|gp| { if let ast::GenericParam::ConstParam(cp) = gp { - cp.name().map_or(false, |n| n.text() == name_ref.text()) + cp.name().is_some_and(|n| n.text() == name_ref.text()) } else { false } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 6735d7dcbe1..0cc807aff64 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -99,7 +99,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let parent = to_extract.syntax().parent().and_then(ast::Expr::cast); // Any expression that autoderefs may need adjustment. - let mut needs_adjust = parent.as_ref().map_or(false, |it| match it { + let mut needs_adjust = parent.as_ref().is_some_and(|it| match it { ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) @@ -336,10 +336,12 @@ impl ExtractionKind { } } + let mut name_generator = + suggest_name::NameGenerator::new_from_scope_locals(ctx.sema.scope(to_extract.syntax())); let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) { - literal_name + name_generator.suggest_name(&literal_name) } else { - suggest_name::for_variable(to_extract, &ctx.sema) + name_generator.for_variable(to_extract, &ctx.sema) }; let var_name = match self { @@ -352,10 +354,10 @@ impl ExtractionKind { } fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> { - let literal = match expr { - ast::Expr::Literal(literal) => literal, - _ => return None, + let ast::Expr::Literal(literal) = expr else { + return None; }; + let inner = match literal.kind() { ast::LiteralKind::String(string) => string.value().ok()?.into_owned(), ast::LiteralKind::ByteString(byte_string) => { @@ -2632,4 +2634,33 @@ fn foo() { "Extract into static", ); } + + #[test] + fn extract_variable_name_conflicts() { + check_assist_by_label( + extract_variable, + r#" +struct S { x: i32 }; + +fn main() { + let s = 2; + let t = $0S { x: 1 }$0; + let t2 = t; + let x = s; +} +"#, + r#" +struct S { x: i32 }; + +fn main() { + let s = 2; + let $0s1 = S { x: 1 }; + let t = s1; + let t2 = t; + let x = s; +} +"#, + "Extract into variable", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index 25076dd5255..7f7db07152d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs @@ -64,7 +64,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let name_ref_value = name_ref?; let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value); match name_ref_class { - Some(NameRefClass::Definition(Definition::Module(m))) => { + Some(NameRefClass::Definition(Definition::Module(m), _)) => { if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index f4b4c22d98d..9d01ec00f83 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -85,7 +85,6 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) let is_unsafe = func_node.unsafe_token().is_some(); let ty = make::ty_fn_ptr( - None, is_unsafe, func_node.abi(), fn_params_vec.into_iter(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 7b95c124e62..8f5daa4125a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -1077,7 +1077,7 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri .filter_map(ast::NameRef::cast) .filter(|name| name.ident_token().is_some()) .last()?; - if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_))) = + if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_), _)) = NameRefClass::classify(sema, &name_ref) { return Some(name_ref.to_string().to_lowercase()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 1dd376ac3fd..5101d8fa0a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -78,7 +78,7 @@ pub(crate) fn move_bounds_to_where_clause( fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); - let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); + let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds()); Some(predicate.clone_for_update()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index 376243c2681..75120768da0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -47,7 +47,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> .parent() // AssocItemList .and_then(|x| x.parent()) .and_then(ast::Impl::cast) - .map_or(false, |imp| imp.trait_().is_some()) + .is_some_and(|imp| imp.trait_().is_some()) { cov_mark::hit!(trait_impl); return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 56dd6cf29ae..b31d45e6d45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -17,7 +17,7 @@ use syntax::{ }; use crate::{ - utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block}, + utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block}, AssistContext, AssistId, AssistKind, Assists, }; @@ -135,7 +135,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' }; let has_preceding_if_expr = - if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind())); + if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` make::block_expr(None, Some(match_expr)).into() @@ -163,7 +163,7 @@ fn make_else_arm( Some(it) => { if does_pat_match_variant(pat, &it.sad_pattern()) { it.happy_pattern_wildcard() - } else if does_nested_pattern(pat) { + } else if does_pat_variant_nested_or_literal(ctx, pat) { make::wildcard_pat().into() } else { it.sad_pattern() @@ -241,7 +241,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal() .map(|it| it.token().kind()) - .map_or(false, |it| it == T![true] || it == T![false]) => + .is_some_and(|it| it == T![true] || it == T![false]) => { "" } @@ -265,12 +265,12 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let condition = match if_let_pat { ast::Pat::LiteralPat(p) - if p.literal().map_or(false, |it| it.token().kind() == T![true]) => + if p.literal().is_some_and(|it| it.token().kind() == T![true]) => { scrutinee } ast::Pat::LiteralPat(p) - if p.literal().map_or(false, |it| it.token().kind() == T![false]) => + if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { make::expr_prefix(T![!], scrutinee) } @@ -339,10 +339,10 @@ fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { ast::Pat::TupleStructPat(it) => it.fields().any(binds_name_v), ast::Pat::RecordPat(it) => it .record_pat_field_list() - .map_or(false, |rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)), - ast::Pat::RefPat(pat) => pat.pat().map_or(false, binds_name_v), - ast::Pat::BoxPat(pat) => pat.pat().map_or(false, binds_name_v), - ast::Pat::ParenPat(pat) => pat.pat().map_or(false, binds_name_v), + .is_some_and(|rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)), + ast::Pat::RefPat(pat) => pat.pat().is_some_and(binds_name_v), + ast::Pat::BoxPat(pat) => pat.pat().is_some_and(binds_name_v), + ast::Pat::ParenPat(pat) => pat.pat().is_some_and(binds_name_v), _ => false, } } @@ -350,7 +350,7 @@ fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { sema.type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted())) - .map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern())) + .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern())) } #[cfg(test)] @@ -702,11 +702,11 @@ fn main() { } #[test] - fn nested_type() { + fn test_if_let_with_match_nested_tuple_struct() { check_assist( replace_if_let_with_match, r#" -//- minicore: result +//- minicore: result, option fn foo(x: Result<i32, ()>) { let bar: Result<_, ()> = Ok(Some(1)); $0if let Ok(Some(_)) = bar { @@ -726,6 +726,700 @@ fn foo(x: Result<i32, ()>) { } "#, ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct(i32, i32); +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2)); + $0if let Ok(MyStruct(a, b)) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct(i32, i32); +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2)); + match bar { + Ok(MyStruct(a, b)) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_slice() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[i32], ()>) { + let foo: Result<&[_], ()> = Ok(&[0, 1, 2]); + $0if let Ok([]) = foo { + () + } else { + () + } +} + "#, + r#" +fn foo(x: Result<&[i32], ()>) { + let foo: Result<&[_], ()> = Ok(&[0, 1, 2]); + match foo { + Ok([]) => (), + _ => (), + } +} + "#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + $0if let Ok([_, "bar"]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + match foobar { + Ok([_, "bar"]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + $0if let Ok([..]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + match foobar { + Ok([..]) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + $0if let Ok([a, ..]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + match foobar { + Ok([a, ..]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + $0if let Ok([a, .., b, c]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + match foobar { + Ok([a, .., b, c]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<Option<[&'static str; 2]>, ()>) { + let foobar: Result<_, ()> = Ok(Some(["foo", "bar"])); + $0if let Ok(Some([_, "bar"])) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<Option<[&'static str; 2]>, ()>) { + let foobar: Result<_, ()> = Ok(Some(["foo", "bar"])); + match foobar { + Ok(Some([_, "bar"])) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_literal() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&'static str, ()>) { + let bar: Result<&_, ()> = Ok("bar"); + $0if let Ok("foo") = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&'static str, ()>) { + let bar: Result<&_, ()> = Ok("bar"); + match bar { + Ok("foo") => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_tuple() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + $0if let Ok((1, second, third)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + match bar { + Ok((1, second, third)) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + $0if let Ok((first, second, third)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + match bar { + Ok((first, second, third)) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_or() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(1 | 2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(1 | 2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + $0if let Ok((b, a) | (a, b)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + match bar { + Ok((b, a) | (a, b)) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + $0if let Ok((1, a) | (a, 2)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + match bar { + Ok((1, a) | (a, 2)) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_range() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(1..2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(1..2) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_paren() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + $0if let Ok(((1, 2))) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + match bar { + Ok(((1, 2))) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + $0if let Ok(((a, b))) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + match bar { + Ok(((a, b))) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_macro() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + macro_rules! is_42 { + () => { + 42 + }; + } + + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(is_42!()) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + macro_rules! is_42 { + () => { + 42 + }; + } + + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(is_42!()) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_path() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +enum MyEnum { + Foo, + Bar, +} + +fn foo(x: Result<MyEnum, ()>) { + let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo); + $0if let Ok(MyEnum::Foo) = bar { + () + } else { + () + } +} +"#, + r#" +enum MyEnum { + Foo, + Bar, +} + +fn foo(x: Result<MyEnum, ()>) { + let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo); + match bar { + Ok(MyEnum::Foo) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_record() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, bar }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, bar }) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, bar: 12 }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, bar: 12 }) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, .. }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result<MyStruct, ()>) { + let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, .. }) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +enum MyEnum { + Foo(i32, i32), + Bar { a: i32, b: i32 }, +} + +fn foo(x: Result<MyEnum, ()>) { + let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2)); + $0if let Ok(MyEnum::Bar { a, b }) = bar { + () + } else { + () + } +} +"#, + r#" +enum MyEnum { + Foo(i32, i32), + Bar { a: i32, b: i32 }, +} + +fn foo(x: Result<MyEnum, ()>) { + let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2)); + match bar { + Ok(MyEnum::Bar { a, b }) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_ident() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(a @ 1..2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(a @ 1..2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(a) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(a) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + $0if let Ok(a @ b @ c @ d) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + let bar: Result<i32, ()> = Ok(1); + match bar { + Ok(a @ b @ c @ d) => (), + Err(_) => (), + } +} +"#, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index a856da09215..47972ff619a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -20,7 +20,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // fn main() { // let x = Some(1); -// if let Some(${0:x}) = x {} +// if let Some(${0:x1}) = x {} // } // ``` pub(crate) fn replace_is_method_with_if_let_method( @@ -40,10 +40,13 @@ pub(crate) fn replace_is_method_with_if_let_method( "is_some" | "is_ok" => { let receiver = call_expr.receiver()?; + let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals( + ctx.sema.scope(if_expr.syntax()), + ); let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() { - path_expr.path()?.to_string() + name_generator.suggest_name(&path_expr.path()?.to_string()) } else { - suggest_name::for_variable(&receiver, &ctx.sema) + name_generator.for_variable(&receiver, &ctx.sema) }; let (assist_id, message, text) = if name_ref.text() == "is_some" { @@ -98,7 +101,7 @@ fn main() { r#" fn main() { let x = Some(1); - if let Some(${0:x}) = x {} + if let Some(${0:x1}) = x {} } "#, ); @@ -150,7 +153,7 @@ fn main() { r#" fn main() { let x = Ok(1); - if let Ok(${0:x}) = x {} + if let Ok(${0:x1}) = x {} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 1101c20f1b7..f026b3230dd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -159,7 +159,7 @@ fn path_eq_no_generics(lhs: ast::Path, rhs: ast::Path) -> bool { && lhs .name_ref() .zip(rhs.name_ref()) - .map_or(false, |(lhs, rhs)| lhs.text() == rhs.text()) => {} + .is_some_and(|(lhs, rhs)| lhs.text() == rhs.text()) => {} _ => return false, } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 648bf358b4b..c6cffb5434a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -65,7 +65,7 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let mut pipe_index = pipe_token.index(); if pipe_token .prev_sibling_or_token() - .map_or(false, |it| it.kind() == SyntaxKind::WHITESPACE) + .is_some_and(|it| it.kind() == SyntaxKind::WHITESPACE) { pipe_index -= 1; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs index 52df30d9623..38ca572fa66 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs @@ -66,7 +66,7 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> { .filter_map(|t| t.path()); let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?; - if final_path.segment().map_or(false, |it| it.self_token().is_some()) { + if final_path.segment().is_some_and(|it| it.self_token().is_some()) { final_path.qualifier() } else { Some(final_path) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 78fdfba6a07..54e42f126bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -2885,7 +2885,7 @@ fn main() { r#####" fn main() { let x = Some(1); - if let Some(${0:x}) = x {} + if let Some(${0:x1}) = x {} } "#####, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 3c26b043597..e20c4ef09e8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -316,43 +316,73 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { pat_head == var_head } -pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool { - let depth = calc_depth(pat, 0); +pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { + check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0) +} + +fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { + ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo| { + ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_))) + }) +} - if 1 < depth { +fn check_pat_variant_nested_or_literal_with_depth( + ctx: &AssistContext<'_>, + pat: &ast::Pat, + depth_after_refutable: usize, +) -> bool { + if depth_after_refutable > 1 { return true; } - false -} -fn calc_depth(pat: &ast::Pat, depth: usize) -> usize { match pat { - ast::Pat::IdentPat(_) - | ast::Pat::BoxPat(_) - | ast::Pat::RestPat(_) - | ast::Pat::LiteralPat(_) + ast::Pat::RestPat(_) | ast::Pat::WildcardPat(_) | ast::Pat::RefPat(_) => false, + + ast::Pat::LiteralPat(_) + | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) - | ast::Pat::OrPat(_) - | ast::Pat::ParenPat(_) | ast::Pat::PathPat(_) - | ast::Pat::WildcardPat(_) - | ast::Pat::RangePat(_) - | ast::Pat::RecordPat(_) - | ast::Pat::RefPat(_) - | ast::Pat::SlicePat(_) - | ast::Pat::TuplePat(_) - | ast::Pat::ConstBlockPat(_) => depth, - - // FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat` - ast::Pat::TupleStructPat(pat) => { - let mut max_depth = depth; - for p in pat.fields() { - let d = calc_depth(&p, depth + 1); - if d > max_depth { - max_depth = d - } - } - max_depth + | ast::Pat::BoxPat(_) + | ast::Pat::ConstBlockPat(_) => true, + + ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::ParenPat(paren_pat) => paren_pat.pat().is_none_or(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::TuplePat(tuple_pat) => tuple_pat.fields().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::RecordPat(record_pat) => { + let adjusted_next_depth = + depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 }; + record_pat.record_pat_field_list().is_none_or(|pat| { + pat.fields().any(|pat| { + pat.pat().is_none_or(|pat| { + check_pat_variant_nested_or_literal_with_depth( + ctx, + &pat, + adjusted_next_depth, + ) + }) + }) + }) + } + ast::Pat::OrPat(or_pat) => or_pat.pats().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::TupleStructPat(tuple_struct_pat) => { + let adjusted_next_depth = + depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 }; + tuple_struct_pat.fields().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, adjusted_next_depth) + }) + } + ast::Pat::SlicePat(slice_pat) => { + let mut pats = slice_pat.pats(); + pats.next() + .is_none_or(|pat| !matches!(pat, ast::Pat::RestPat(_)) || pats.next().is_some()) } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c5a91e478bf..75caf6d49f7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -32,7 +32,7 @@ pub(crate) fn gen_trait_fn_body( /// Generate a `Clone` impl based on the fields and members of the target type. fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "clone")); + stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)) @@ -344,7 +344,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `Hash` impl based on the fields and members of the target type. fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "hash")); + stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -400,7 +400,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `PartialEq` impl based on the fields and members of the target type. fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "eq")); + stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -592,7 +592,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) - } fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp")); + stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> { let mut arms = vec![]; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 229ce7723b5..26074672ba9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,6 +1,8 @@ //! Completes references after dot (fields and method calls). -use hir::{sym, Name}; +use std::ops::ControlFlow; + +use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -158,21 +160,55 @@ fn complete_fields( fn complete_methods( ctx: &CompletionContext<'_>, receiver: &hir::Type, - mut f: impl FnMut(hir::Function), + f: impl FnMut(hir::Function), ) { - let mut seen_methods = FxHashSet::default(); - receiver.iterate_method_candidates_with_traits( + struct Callback<'a, F> { + ctx: &'a CompletionContext<'a>, + f: F, + seen_methods: FxHashSet<Name>, + } + + impl<F> MethodCandidateCallback for Callback<'_, F> + where + F: FnMut(hir::Function), + { + // We don't want to exclude inherent trait methods - that is, methods of traits available from + // `where` clauses or `dyn Trait`. + fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { + if func.self_param(self.ctx.db).is_some() + && self.seen_methods.insert(func.name(self.ctx.db)) + { + (self.f)(func); + } + ControlFlow::Continue(()) + } + + fn on_trait_method(&mut self, func: hir::Function) -> ControlFlow<()> { + // This needs to come before the `seen_methods` test, so that if we see the same method twice, + // once as inherent and once not, we will include it. + if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) { + if self.ctx.exclude_traits.contains(&trait_) { + return ControlFlow::Continue(()); + } + } + + if func.self_param(self.ctx.db).is_some() + && self.seen_methods.insert(func.name(self.ctx.db)) + { + (self.f)(func); + } + + ControlFlow::Continue(()) + } + } + + receiver.iterate_method_candidates_split_inherent( ctx.db, &ctx.scope, &ctx.traits_in_scope(), Some(ctx.module), None, - |func| { - if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { - f(func); - } - None::<()> - }, + Callback { ctx, f, seen_methods: FxHashSet::default() }, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index c9013d1d17d..0b6790d42a6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -43,7 +43,7 @@ pub(crate) fn complete_cargo_env_vars( .sema .hir_file_for(&expanded.syntax().parent()?) .macro_file() - .map_or(false, |it| it.is_env_or_option_env(ctx.sema.db)); + .is_some_and(|it| it.is_env_or_option_env(ctx.sema.db)); if !is_in_env_expansion { let call = macro_call_for_string_token(expanded)?; let makro = ctx.sema.resolve_macro_call(&call)?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index ff2c8da4213..c2e5eefe101 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,9 @@ //! Completion of names from the current scope in expression position. -use hir::{sym, Name, ScopeDef}; +use std::ops::ControlFlow; + +use hir::{sym, Name, PathCandidateCallback, ScopeDef}; +use ide_db::FxHashSet; use syntax::ast; use crate::{ @@ -9,6 +12,38 @@ use crate::{ CompletionContext, Completions, }; +struct PathCallback<'a, F> { + ctx: &'a CompletionContext<'a>, + acc: &'a mut Completions, + add_assoc_item: F, + seen: FxHashSet<hir::AssocItem>, +} + +impl<F> PathCandidateCallback for PathCallback<'_, F> +where + F: FnMut(&mut Completions, hir::AssocItem), +{ + fn on_inherent_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + if self.seen.insert(item) { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } + + fn on_trait_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + // The excluded check needs to come before the `seen` test, so that if we see the same method twice, + // once as inherent and once not, we will include it. + if item + .container_trait(self.ctx.db) + .is_none_or(|trait_| !self.ctx.exclude_traits.contains(&trait_)) + && self.seen.insert(item) + { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } +} + pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -50,12 +85,18 @@ pub(crate) fn complete_expr_path( }; match qualified { + // We exclude associated types/consts of excluded traits here together with methods, + // even though we don't exclude them when completing in type position, because it's easier. Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() - .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) + .copied() + .map(hir::Trait::from) + .filter(|it| !ctx.exclude_traits.contains(it)) + .flat_map(|it| it.items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + // Don't filter excluded traits here, user requested this specific trait. trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) } Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { @@ -64,9 +105,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -121,9 +167,14 @@ pub(crate) fn complete_expr_path( // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. // (where AssocType is defined on a trait, not an inherent impl) - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -134,6 +185,7 @@ pub(crate) fn complete_expr_path( }); } hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { + // Don't filter excluded traits here, user requested this specific trait. // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. for item in t.items(ctx.db) { add_assoc_item(acc, item); @@ -151,9 +203,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); } _ => (), } @@ -236,9 +293,17 @@ pub(crate) fn complete_expr_path( [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), } } + // synthetic names currently leak out as we lack synthetic hygiene, so filter them + // out here + ScopeDef::Local(_) => { + if !name.as_str().starts_with('<') { + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) + } + } _ if scope_def_applicable(def) => { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } + _ => (), }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 847fa4cf889..bcfda928c4d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs @@ -46,7 +46,7 @@ pub(crate) fn complete_extern_abi( ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { - if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { + if !expanded.syntax().parent().is_some_and(|it| ast::Abi::can_cast(it.kind())) { return None; } let abi_str = expanded; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 2a6b310d3a2..3b2b2fd706e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -8,6 +8,7 @@ use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T}; use crate::{ + config::AutoImportExclusionType, context::{ CompletionContext, DotAccess, PathCompletionCtx, PathKind, PatternContext, Qualified, TypeLocation, @@ -267,6 +268,19 @@ fn import_on_the_fly( && !ctx.is_item_hidden(original_item) && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) }) + .filter(|import| { + let def = import.item_to_import.into_module_def(); + if let Some(&kind) = ctx.exclude_flyimport.get(&def) { + if kind == AutoImportExclusionType::Always { + return false; + } + let method_imported = import.item_to_import != import.original_item; + if method_imported { + return false; + } + } + true + }) .sorted_by(|a, b| { let key = |import_path| { ( @@ -352,6 +366,24 @@ fn import_on_the_fly_method( !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(&import.original_item) }) + .filter(|import| { + let def = import.item_to_import.into_module_def(); + if let Some(&kind) = ctx.exclude_flyimport.get(&def) { + if kind == AutoImportExclusionType::Always { + return false; + } + let method_imported = import.item_to_import != import.original_item; + if method_imported { + return false; + } + } + + if let ModuleDef::Trait(_) = import.item_to_import.into_module_def() { + !ctx.exclude_flyimport.contains_key(&def) + } else { + true + } + }) .sorted_by(|a, b| { let key = |import_path| { ( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index ee3b817ee8c..e86eaad4d0f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -167,7 +167,7 @@ fn should_add_self_completions( return false; } match param_list.params().next() { - Some(first) => first.pat().map_or(false, |pat| pat.syntax().text_range().contains(cursor)), + Some(first) => first.pat().is_some_and(|pat| pat.syntax().text_range().contains(cursor)), None => true, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 117a5e3d935..d0c4c24d060 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -93,7 +93,7 @@ pub(crate) fn add_default_update( let default_trait = ctx.famous_defs().core_default_Default(); let impls_default_trait = default_trait .zip(ty.as_ref()) - .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); + .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); if impls_default_trait { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 45704549e60..9d62622add2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -101,7 +101,7 @@ pub(crate) fn complete_use_path( ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { // exclude prelude enum let is_builtin = - res.krate(ctx.db).map_or(false, |krate| krate.is_builtin(ctx.db)); + res.krate(ctx.db).is_some_and(|krate| krate.is_builtin(ctx.db)); if !is_builtin { let item = CompletionItem::new( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 1d05419c96d..8b1ce11e8a4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -10,7 +10,7 @@ use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap}; use crate::{snippet::Snippet, CompletionFieldsToResolve}; #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CompletionConfig { +pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, @@ -28,6 +28,14 @@ pub struct CompletionConfig { pub snippets: Vec<Snippet>, pub limit: Option<usize>, pub fields_to_resolve: CompletionFieldsToResolve, + pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>, + pub exclude_traits: &'a [String], +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum AutoImportExclusionType { + Always, + Methods, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -36,7 +44,7 @@ pub enum CallableSnippets { AddParentheses, } -impl CompletionConfig { +impl CompletionConfig<'_> { pub fn postfix_snippets(&self) -> impl Iterator<Item = (&str, &Snippet)> { self.snippets .iter() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f8d403122d1..3705e2c73d6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -7,8 +7,8 @@ mod tests; use std::{iter, ops::ControlFlow}; use hir::{ - HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, - Symbol, Type, TypeInfo, + HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics, + SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition, @@ -22,6 +22,7 @@ use syntax::{ }; use crate::{ + config::AutoImportExclusionType, context::analysis::{expand_and_analyze, AnalysisResult}, CompletionConfig, }; @@ -429,7 +430,7 @@ pub(crate) struct CompletionContext<'a> { pub(crate) sema: Semantics<'a, RootDatabase>, pub(crate) scope: SemanticsScope<'a>, pub(crate) db: &'a RootDatabase, - pub(crate) config: &'a CompletionConfig, + pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, /// The token before the cursor, in the original file. @@ -462,6 +463,17 @@ pub(crate) struct CompletionContext<'a> { /// Here depth will be 2 pub(crate) depth_from_crate_root: usize, + /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest + /// importing those traits. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>, + /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`). + /// They will *not* be excluded, however, if they are available as a generic bound. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_traits: FxHashSet<hir::Trait>, + /// Whether and how to complete semicolon for unit-returning functions. pub(crate) complete_semicolon: CompleteSemicolon, } @@ -670,7 +682,7 @@ impl<'a> CompletionContext<'a> { pub(crate) fn new( db: &'a RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, - config: &'a CompletionConfig, + config: &'a CompletionConfig<'a>, ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); @@ -742,17 +754,53 @@ impl<'a> CompletionContext<'a> { let mut locals = FxHashMap::default(); scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { + // synthetic names currently leak out as we lack synthetic hygiene, so filter them + // out here + if name.as_str().starts_with('<') { + return; + } locals.insert(name, local); } }); let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db)) - // `BlockExpr` modules are not count as module depth + // `BlockExpr` modules do not count towards module depth .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_))) .count() // exclude `m` itself .saturating_sub(1); + let exclude_traits: FxHashSet<_> = config + .exclude_traits + .iter() + .filter_map(|path| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .find_map(|it| match it { + hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t), + _ => None, + }) + }) + .collect(); + + let mut exclude_flyimport: FxHashMap<_, _> = config + .exclude_flyimport + .iter() + .flat_map(|(path, kind)| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .map(|it| (it.into_module_def(), *kind)) + }) + .collect(); + exclude_flyimport + .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + let complete_semicolon = if config.add_semicolon_to_unit { let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| { match_ast! { @@ -817,6 +865,8 @@ impl<'a> CompletionContext<'a> { qualifier_ctx, locals, depth_from_crate_root, + exclude_flyimport, + exclude_traits, complete_semicolon, }; Some((ctx, analysis)) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 1c4cbb25b1f..acce62a041c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -417,6 +417,15 @@ fn analyze( derive_ctx, } = expansion_result; + if original_token.kind() != self_token.kind() + // FIXME: This check can be removed once we use speculative database forking for completions + && !(original_token.kind().is_punct() || original_token.kind().is_trivia()) + && !(SyntaxKind::is_any_identifier(original_token.kind()) + && SyntaxKind::is_any_identifier(self_token.kind())) + { + return None; + } + // Overwrite the path kind for derives if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some(ast::NameLike::NameRef(name_ref)) = @@ -456,7 +465,7 @@ fn analyze( && p.ancestors().any(|it| it.kind() == SyntaxKind::META) { let colon_prefix = previous_non_trivia_token(self_token.clone()) - .map_or(false, |it| T![:] == it.kind()); + .is_some_and(|it| T![:] == it.kind()); CompletionAnalysis::UnexpandedAttrTT { fake_attribute_under_caret: fake_ident_token @@ -643,7 +652,7 @@ fn expected_type_and_name( // match foo { $0 } // match foo { ..., pat => $0 } ast::MatchExpr(it) => { - let on_arrow = previous_non_trivia_token(token.clone()).map_or(false, |it| T![=>] == it.kind()); + let on_arrow = previous_non_trivia_token(token.clone()).is_some_and(|it| T![=>] == it.kind()); let ty = if on_arrow { // match foo { ..., pat => $0 } @@ -780,7 +789,7 @@ fn classify_name_ref( if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone()) - .map_or(false, |it| T![.] == it.kind()); + .is_some_and(|it| T![.] == it.kind()); return find_node_in_file_compensated( sema, @@ -814,7 +823,7 @@ fn classify_name_ref( let receiver_is_ambiguous_float_literal = match &receiver { Some(ast::Expr::Literal(l)) => matches! { l.kind(), - ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.')) + ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().is_some_and(|it| it.text().ends_with('.')) }, _ => false, }; @@ -846,7 +855,7 @@ fn classify_name_ref( let receiver = find_opt_node_in_file(original_file, method.receiver()); let kind = NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), - kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, + kind: DotAccessKind::Method { has_parens: method.arg_list().is_some_and(|it| it.l_paren_token().is_some()) }, receiver, ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) } }); @@ -1193,13 +1202,13 @@ fn classify_name_ref( let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .map_or(false, |it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { Some(arm) => arm .fat_arrow_token() - .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()), + .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()), None => false, }; @@ -1329,7 +1338,7 @@ fn classify_name_ref( } } - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); make_path_kind_expr(it.into()) }, @@ -1358,7 +1367,7 @@ fn classify_name_ref( match parent { ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); make_path_kind_expr(it.into()) }, @@ -1612,8 +1621,7 @@ fn pattern_context_for( &pat, ast::Pat::IdentPat(it) if it.syntax() - .parent() - .map_or(false, |node| { + .parent().is_some_and(|node| { let kind = node.kind(); ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 9608eed99d8..b91f915619d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -631,7 +631,7 @@ impl Builder { self.set_documentation(Some(docs)) } pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder { - self.documentation = docs.map(Into::into); + self.documentation = docs; self } pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 14f42b40055..ca6c9ad9f08 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -31,7 +31,7 @@ use crate::{ }; pub use crate::{ - config::{CallableSnippets, CompletionConfig}, + config::{AutoImportExclusionType, CallableSnippets, CompletionConfig}, item::{ CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, CompletionRelevanceTypeMatch, @@ -184,7 +184,7 @@ impl CompletionFieldsToResolve { /// analysis. pub fn completions( db: &RootDatabase, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, trigger_character: Option<char>, ) -> Option<Vec<CompletionItem>> { @@ -269,7 +269,7 @@ pub fn completions( /// This is used for import insertion done via completions like flyimport and custom user snippets. pub fn resolve_completion_edits( db: &RootDatabase, - config: &CompletionConfig, + config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, imports: impl IntoIterator<Item = (String, String)>, ) -> Option<Vec<TextEdit>> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index baa30b28630..c239ca512da 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -86,11 +86,7 @@ impl<'a> RenderContext<'a> { fn is_immediately_after_macro_bang(&self) -> bool { self.completion.token.kind() == SyntaxKind::BANG - && self - .completion - .token - .parent() - .map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL) + && self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL) } fn is_deprecated(&self, def: impl HasAttrs) -> bool { @@ -636,7 +632,7 @@ fn compute_type_match( } fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { - ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name) + ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) } fn compute_ref_match( @@ -778,7 +774,7 @@ mod tests { relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet", ), - (relevance.trait_.map_or(false, |it| it.is_op_method), "op_method"), + (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), ] .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 5265aa8515b..04bb178c658 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -100,9 +100,9 @@ // } // ---- +use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; use itertools::Itertools; -use syntax::{ast, AstNode, GreenNode, SyntaxNode}; use crate::context::CompletionContext; @@ -123,10 +123,7 @@ pub struct Snippet { pub scope: SnippetScope, pub description: Option<Box<str>>, snippet: String, - // These are `ast::Path`'s but due to SyntaxNodes not being Send we store these - // and reconstruct them on demand instead. This is cheaper than reparsing them - // from strings - requires: Box<[GreenNode]>, + requires: Box<[ModPath]>, } impl Snippet { @@ -143,7 +140,6 @@ impl Snippet { } let (requires, snippet, description) = validate_snippet(snippet, description, requires)?; Some(Snippet { - // Box::into doesn't work as that has a Copy bound 😒 postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(), prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(), scope, @@ -167,15 +163,11 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<Vec<LocatedImport>> { +fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> { let import_cfg = ctx.config.import_path_config(); - let resolve = |import: &GreenNode| { - let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?; - let item = match ctx.scope.speculative_resolve(&path)? { - hir::PathResolution::Def(def) => def.into(), - _ => return None, - }; + let resolve = |import| { + let item = ctx.scope.resolve_mod_path(import).next()?; let path = ctx.module.find_use_path( ctx.db, item, @@ -198,19 +190,14 @@ fn validate_snippet( snippet: &[String], description: &str, requires: &[String], -) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> { +) -> Option<(Box<[ModPath]>, String, Option<Box<str>>)> { let mut imports = Vec::with_capacity(requires.len()); for path in requires.iter() { - let use_path = - ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT_FIXME) - .syntax_node() - .descendants() - .find_map(ast::Path::cast)?; - if use_path.syntax().text() != path.as_str() { - return None; - } - let green = use_path.syntax().green().into_owned(); - imports.push(green); + let use_path = ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + ); + imports.push(use_path); } let snippet = snippet.iter().join("\n"); let description = (!description.is_empty()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index e01097a9105..1815f340532 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -61,7 +61,7 @@ fn function() {} union Union { field: i32 } "#; -pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { +pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { enable_postfix_completions: true, enable_imports_on_the_fly: true, enable_self_on_the_fly: true, @@ -85,6 +85,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { snippets: Vec::new(), limit: None, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; pub(crate) fn completion_list(ra_fixture: &str) -> String { @@ -109,7 +111,7 @@ pub(crate) fn completion_list_with_trigger_character( } fn completion_list_with_config_raw( - config: CompletionConfig, + config: CompletionConfig<'_>, ra_fixture: &str, include_keywords: bool, trigger_character: Option<char>, @@ -132,7 +134,7 @@ fn completion_list_with_config_raw( } fn completion_list_with_config( - config: CompletionConfig, + config: CompletionConfig<'_>, ra_fixture: &str, include_keywords: bool, trigger_character: Option<char>, @@ -161,7 +163,7 @@ pub(crate) fn do_completion(code: &str, kind: CompletionItemKind) -> Vec<Complet } pub(crate) fn do_completion_with_config( - config: CompletionConfig, + config: CompletionConfig<'_>, code: &str, kind: CompletionItemKind, ) -> Vec<CompletionItem> { @@ -220,7 +222,7 @@ pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: #[track_caller] pub(crate) fn check_edit_with_config( - config: CompletionConfig, + config: CompletionConfig<'_>, what: &str, ra_fixture_before: &str, ra_fixture_after: &str, @@ -257,7 +259,7 @@ fn check_empty(ra_fixture: &str, expect: Expect) { } pub(crate) fn get_all_items( - config: CompletionConfig, + config: CompletionConfig<'_>, code: &str, trigger_character: Option<char>, ) -> Vec<CompletionItem> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index acafa6518f6..ebf35820570 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -713,6 +713,28 @@ struct Foo; ); } +#[test] +fn issue_17479() { + check( + r#" +//- proc_macros: issue_17479 +fn main() { + proc_macros::issue_17479!("te$0"); +} +"#, + expect![""], + ); + check( + r#" +//- proc_macros: issue_17479 +fn main() { + proc_macros::issue_17479!("$0"); +} +"#, + expect![""], + ) +} + mod cfg { use super::*; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index ea1b7ad7871..30466148686 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1,13 +1,30 @@ //! Completion tests for expressions. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; +use crate::{ + config::AutoImportExclusionType, + tests::{ + check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE, + TEST_CONFIG, + }, + CompletionConfig, +}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); expect.assert_eq(&actual) } +fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) { + let actual = completion_list_with_config( + config, + &format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), + true, + None, + ); + expect.assert_eq(&actual) +} + #[test] fn complete_literal_struct_with_a_private_field() { // `FooDesc.bar` is private, the completion should not be triggered. @@ -1390,3 +1407,453 @@ fn main() { "#]], ); } + +#[test] +fn excluded_trait_method_is_excluded() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_not_excluded_when_inherent() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +fn foo(v: &dyn ExcludedTrait) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +fn foo(v: impl ExcludedTrait) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +fn foo<T: ExcludedTrait>(v: T) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_excluded_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl<T> ExcludedTrait for T {} +} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (use module2::ExcludedTrait) fn(&self) + me baz() (use module2::ExcludedTrait) fn(&self) + me foo() (use module2::ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn flyimport_excluded_trait_method_is_excluded_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "test::module2::ExcludedTrait".to_owned(), + AutoImportExclusionType::Methods, + )], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl<T> ExcludedTrait for T {} +} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (use module2::ExcludedTrait) fn(&self) + me baz() (use module2::ExcludedTrait) fn(&self) + me foo() (use module2::ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_excluded_from_path_completion() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + me inherent(…) fn(&self) + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_not_excluded_when_trait_is_specified() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + ExcludedTrait::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + <Foo as ExcludedTrait>::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); +} + +#[test] +fn excluded_trait_not_excluded_when_inherent_path() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +fn foo() { + <dyn ExcludedTrait>::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl<T> ExcludedTrait for T {} + +fn foo<T: ExcludedTrait>() { + T::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); +} + +#[test] +fn hide_ragennew_synthetic_identifiers() { + check_empty( + r#" +//- minicore: iterator +fn bar() { + for i in [0; 10] { + r$0 + } +} + "#, + expect![[r#" + en Option Option<{unknown}> + en Result Result<{unknown}, {unknown}> + fn bar() fn() + lc i i32 + ma const_format_args!(…) macro_rules! const_format_args + ma format_args!(…) macro_rules! format_args + ma format_args_nl!(…) macro_rules! format_args_nl + ma panic!(…) macro_rules! panic + ma print!(…) macro_rules! print + md core + md result (use core::result) + md rust_2015 (use core::prelude::rust_2015) + md rust_2018 (use core::prelude::rust_2018) + md rust_2021 (use core::prelude::rust_2021) + tt Clone + tt Copy + tt IntoIterator + tt Iterator + ta Result (use core::fmt::Result) + ev Err(…) Err(E) + ev None None + ev Ok(…) Ok(T) + ev Some(…) Some(T) + bt u32 u32 + kw async + kw break + kw const + kw continue + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 447dbc998b5..d413977f7c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -3,10 +3,14 @@ use expect_test::{expect, Expect}; use crate::{ context::{CompletionAnalysis, NameContext, NameKind, NameRefKind}, tests::{check_edit, check_edit_with_config, TEST_CONFIG}, + CompletionConfig, }; fn check(ra_fixture: &str, expect: Expect) { - let config = TEST_CONFIG; + check_with_config(TEST_CONFIG, ra_fixture, expect); +} + +fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) { let (db, position) = crate::tests::position(ra_fixture); let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); @@ -1762,3 +1766,31 @@ fn function() { expect![""], ); } + +#[test] +fn excluded_trait_item_included_when_exact_match() { + check_with_config( + CompletionConfig { + exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl<T> ExcludedTrait for T {} +} + +fn foo() { + true.foo$0 +} + "#, + expect![[r#" + me foo() (use module2::ExcludedTrait) fn(&self) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index f34f3d0fc2f..79561a0419f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -241,3 +241,75 @@ impl Copy for S where $0 "#, ); } + +#[test] +fn test_is_not_considered_macro() { + check( + r#" +#[rustc_builtin] +pub macro test($item:item) { + /* compiler built-in */ +} + +macro_rules! expand_to_test { + ( $i:ident ) => { + #[test] + fn foo() { $i; } + }; +} + +fn bar() { + let value = 5; + expand_to_test!(v$0); +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn bar() fn() + fn foo() fn() + fn function() fn() + ma expand_to_test!(…) macro_rules! expand_to_test + ma makro!(…) macro_rules! makro + ma test!(…) macro test + md module + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 388af48c68b..6cfb2231a99 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1345,7 +1345,7 @@ struct Foo<T: PartialOrd } fn check_signatures(src: &str, kind: CompletionItemKind, reduced: Expect, full: Expect) { - const FULL_SIGNATURES_CONFIG: crate::CompletionConfig = { + const FULL_SIGNATURES_CONFIG: crate::CompletionConfig<'_> = { let mut x = TEST_CONFIG; x.full_function_signatures = true; x diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 42a80d63b1a..7482491fc63 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -48,7 +48,7 @@ pub fn callable_for_token( let parent = token.parent()?; let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| { it.arg_list() - .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())) + .is_some_and(|it| it.syntax().text_range().contains(token.text_range().start())) })?; callable_for_node(sema, &calling_node, &token) @@ -136,7 +136,7 @@ pub fn generic_def_for_node( let first_arg_is_non_lifetime = generic_arg_list .generic_args() .next() - .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_))); + .is_some_and(|arg| !matches!(arg, ast::GenericArg::LifetimeArg(_))); Some((def, active_param, first_arg_is_non_lifetime, variant)) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 932ca373020..2d30bb41273 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -13,9 +13,10 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, - Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, - Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, + Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility, + HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, + Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TraitAlias, + TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; use stdx::{format_to, impl_from}; @@ -96,9 +97,39 @@ impl Definition { } pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> { + fn container_to_definition(container: ItemContainer) -> Option<Definition> { + match container { + ItemContainer::Trait(it) => Some(it.into()), + ItemContainer::Impl(it) => Some(it.into()), + ItemContainer::Module(it) => Some(it.into()), + ItemContainer::ExternBlock() | ItemContainer::Crate(_) => None, + } + } match self { + Definition::Macro(it) => Some(it.module(db).into()), + Definition::Module(it) => it.parent(db).map(Definition::Module), + Definition::Field(it) => Some(it.parent_def(db).into()), + Definition::Function(it) => container_to_definition(it.container(db)), + Definition::Adt(it) => Some(it.module(db).into()), + Definition::Const(it) => container_to_definition(it.container(db)), + Definition::Static(it) => container_to_definition(it.container(db)), + Definition::Trait(it) => container_to_definition(it.container(db)), + Definition::TraitAlias(it) => container_to_definition(it.container(db)), + Definition::TypeAlias(it) => container_to_definition(it.container(db)), + Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()), + Definition::SelfType(it) => Some(it.module(db).into()), Definition::Local(it) => it.parent(db).try_into().ok(), - _ => None, + Definition::GenericParam(it) => Some(it.parent().into()), + Definition::Label(it) => it.parent(db).try_into().ok(), + Definition::ExternCrateDecl(it) => container_to_definition(it.container(db)), + Definition::DeriveHelper(it) => Some(it.derive().module(db).into()), + Definition::InlineAsmOperand(it) => it.parent(db).try_into().ok(), + Definition::BuiltinAttr(_) + | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::TupleField(_) + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => None, } } @@ -304,7 +335,7 @@ fn find_std_module( let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(); std_root_module.children(db).find(|module| { - module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name) + module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) }) } @@ -359,24 +390,32 @@ impl IdentClass { .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass)) } - pub fn definitions(self) -> ArrayVec<Definition, 2> { + pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution>), 2> { let mut res = ArrayVec::new(); match self { IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { - res.push(it) + res.push((it, None)) } - IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { - res.push(Definition::Local(local_def)); - res.push(Definition::Field(field_ref)); + IdentClass::NameClass(NameClass::PatFieldShorthand { + local_def, + field_ref, + adt_subst, + }) => { + res.push((Definition::Local(local_def), None)); + res.push((Definition::Field(field_ref), Some(adt_subst))); } - IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), - IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { - res.push(Definition::Local(local_ref)); - res.push(Definition::Field(field_ref)); + IdentClass::NameRefClass(NameRefClass::Definition(it, subst)) => res.push((it, subst)), + IdentClass::NameRefClass(NameRefClass::FieldShorthand { + local_ref, + field_ref, + adt_subst, + }) => { + res.push((Definition::Local(local_ref), None)); + res.push((Definition::Field(field_ref), Some(adt_subst))); } IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => { - res.push(Definition::ExternCrateDecl(decl)); - res.push(Definition::Module(krate.root_module())); + res.push((Definition::ExternCrateDecl(decl), None)); + res.push((Definition::Module(krate.root_module()), None)); } IdentClass::Operator( OperatorClass::Await(func) @@ -384,9 +423,9 @@ impl IdentClass { | OperatorClass::Bin(func) | OperatorClass::Index(func) | OperatorClass::Try(func), - ) => res.push(Definition::Function(func)), + ) => res.push((Definition::Function(func), None)), IdentClass::Operator(OperatorClass::Range(struct0)) => { - res.push(Definition::Adt(Adt::Struct(struct0))) + res.push((Definition::Adt(Adt::Struct(struct0)), None)) } } res @@ -398,12 +437,20 @@ impl IdentClass { IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { res.push(it) } - IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { + IdentClass::NameClass(NameClass::PatFieldShorthand { + local_def, + field_ref, + adt_subst: _, + }) => { res.push(Definition::Local(local_def)); res.push(Definition::Field(field_ref)); } - IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), - IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { + IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it), + IdentClass::NameRefClass(NameRefClass::FieldShorthand { + local_ref, + field_ref, + adt_subst: _, + }) => { res.push(Definition::Local(local_ref)); res.push(Definition::Field(field_ref)); } @@ -437,6 +484,7 @@ pub enum NameClass { PatFieldShorthand { local_def: Local, field_ref: Field, + adt_subst: GenericSubstitution, }, } @@ -446,7 +494,7 @@ impl NameClass { let res = match self { NameClass::Definition(it) => it, NameClass::ConstReference(_) => return None, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }; @@ -517,10 +565,13 @@ impl NameClass { let pat_parent = ident_pat.syntax().parent(); if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) { if record_pat_field.name_ref().is_none() { - if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) { + if let Some((field, _, adt_subst)) = + sema.resolve_record_pat_field_with_subst(&record_pat_field) + { return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field, + adt_subst, }); } } @@ -629,10 +680,11 @@ impl OperatorClass { /// reference to point to two different defs. #[derive(Debug)] pub enum NameRefClass { - Definition(Definition), + Definition(Definition, Option<GenericSubstitution>), FieldShorthand { local_ref: Local, field_ref: Field, + adt_subst: GenericSubstitution, }, /// The specific situation where we have an extern crate decl without a rename /// Here we have both a declaration and a reference. @@ -657,12 +709,16 @@ impl NameRefClass { let parent = name_ref.syntax().parent()?; if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { - if let Some((field, local, _)) = sema.resolve_record_field(&record_field) { + if let Some((field, local, _, adt_subst)) = + sema.resolve_record_field_with_substitution(&record_field) + { let res = match local { - None => NameRefClass::Definition(Definition::Field(field)), - Some(local) => { - NameRefClass::FieldShorthand { field_ref: field, local_ref: local } - } + None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)), + Some(local) => NameRefClass::FieldShorthand { + field_ref: field, + local_ref: local, + adt_subst, + }, }; return Some(res); } @@ -674,44 +730,43 @@ impl NameRefClass { // Only use this to resolve to macro calls for last segments as qualifiers resolve // to modules below. if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { - return Some(NameRefClass::Definition(Definition::Macro(macro_def))); + return Some(NameRefClass::Definition(Definition::Macro(macro_def), None)); } } } - return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition); + return sema + .resolve_path_with_subst(&path) + .map(|(res, subst)| NameRefClass::Definition(res.into(), subst)); } match_ast! { match parent { ast::MethodCallExpr(method_call) => { sema.resolve_method_call_fallback(&method_call) - .map(|it| { - it.map_left(Definition::Function) - .map_right(Definition::Field) - .either(NameRefClass::Definition, NameRefClass::Definition) + .map(|(def, subst)| { + match def { + Either::Left(def) => NameRefClass::Definition(def.into(), subst), + Either::Right(def) => NameRefClass::Definition(def.into(), subst), + } }) }, ast::FieldExpr(field_expr) => { sema.resolve_field_fallback(&field_expr) - .map(|it| { - NameRefClass::Definition(match it { - Either::Left(Either::Left(field)) => Definition::Field(field), - Either::Left(Either::Right(field)) => Definition::TupleField(field), - Either::Right(fun) => Definition::Function(fun), + .map(|(def, subst)| { + match def { + Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst), + Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst), + Either::Right(def) => NameRefClass::Definition(def.into(), subst), + } }) - }) }, ast::RecordPatField(record_pat_field) => { - sema.resolve_record_pat_field(&record_pat_field) - .map(|(field, ..)|field) - .map(Definition::Field) - .map(NameRefClass::Definition) + sema.resolve_record_pat_field_with_subst(&record_pat_field) + .map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst))) }, ast::RecordExprField(record_expr_field) => { - sema.resolve_record_field(&record_expr_field) - .map(|(field, ..)|field) - .map(Definition::Field) - .map(NameRefClass::Definition) + sema.resolve_record_field_with_substitution(&record_expr_field) + .map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst))) }, ast::AssocTypeArg(_) => { // `Trait<Assoc = Ty>` @@ -728,28 +783,30 @@ impl NameRefClass { }) .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) { - return Some(NameRefClass::Definition(Definition::TypeAlias(ty))); + // No substitution, this can only occur in type position. + return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); } } None }, ast::UseBoundGenericArgs(_) => { + // No substitution, this can only occur in type position. sema.resolve_use_type_arg(name_ref) .map(GenericParam::TypeParam) .map(Definition::GenericParam) - .map(NameRefClass::Definition) + .map(|it| NameRefClass::Definition(it, None)) }, ast::ExternCrate(extern_crate_ast) => { let extern_crate = sema.to_def(&extern_crate_ast)?; let krate = extern_crate.resolved_crate(sema.db)?; Some(if extern_crate_ast.rename().is_some() { - NameRefClass::Definition(Definition::Module(krate.root_module())) + NameRefClass::Definition(Definition::Module(krate.root_module()), None) } else { NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } }) }, ast::AsmRegSpec(_) => { - Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()))) + Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None)) }, _ => None } @@ -762,13 +819,17 @@ impl NameRefClass { ) -> Option<NameRefClass> { let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered(); if lifetime.text() == "'static" { - return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime))); + return Some(NameRefClass::Definition( + Definition::BuiltinLifetime(StaticLifetime), + None, + )); } let parent = lifetime.syntax().parent()?; match parent.kind() { - SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { - sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition) - } + SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema + .resolve_label(lifetime) + .map(Definition::Label) + .map(|it| NameRefClass::Definition(it, None)), SyntaxKind::LIFETIME_ARG | SyntaxKind::USE_BOUND_GENERIC_ARGS | SyntaxKind::SELF_PARAM @@ -778,7 +839,7 @@ impl NameRefClass { .resolve_lifetime_param(lifetime) .map(GenericParam::LifetimeParam) .map(Definition::GenericParam) - .map(NameRefClass::Definition), + .map(|it| NameRefClass::Definition(it, None)), _ => None, } } @@ -901,3 +962,17 @@ impl TryFrom<DefWithBody> for Definition { } } } + +impl From<GenericDef> for Definition { + fn from(def: GenericDef) -> Self { + match def { + GenericDef::Function(it) => it.into(), + GenericDef::Adt(it) => it.into(), + GenericDef::Trait(it) => it.into(), + GenericDef::TraitAlias(it) => it.into(), + GenericDef::TypeAlias(it) => it.into(), + GenericDef::Impl(it) => it.into(), + GenericDef::Const(it) => it.into(), + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index b52a325790b..a0ef0f90a65 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -226,9 +226,8 @@ impl HasDocs for hir::AssocItem { impl HasDocs for hir::ExternCrateDecl { fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { - let crate_docs = - docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from); - let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from); + let crate_docs = docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)); + let decl_docs = docs_from_attrs(&self.attrs(db)); match (decl_docs, crate_docs) { (None, None) => None, (Some(decl_docs), None) => Some(decl_docs), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index dab36bf20b9..8f0be1d9035 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -706,7 +706,7 @@ fn path_import_candidate( Some(match qualifier { Some(qualifier) => match sema.resolve_path(&qualifier) { Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => { - if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) { + if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index fc86d169a24..8e25ad3472d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -72,9 +72,7 @@ impl ImportScope { fn from(syntax: SyntaxNode) -> Option<Self> { use syntax::match_ast; fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool { - attrs - .attrs() - .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg")) + attrs.attrs().any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) } match_ast! { match syntax { @@ -102,9 +100,7 @@ impl ImportScope { sema: &Semantics<'_, RootDatabase>, ) -> Option<Self> { fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool { - attrs - .attrs() - .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg")) + attrs.attrs().any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) } // Walk up the ancestor tree searching for a suitable node to do insertions on @@ -487,7 +483,7 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { .contains(&token.kind()) } }) - .filter(|child| child.as_token().map_or(true, |t| t.kind() != SyntaxKind::WHITESPACE)) + .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) .last() { cov_mark::hit!(insert_empty_inner_attr); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 4c197b45338..9e89dfe87ab 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -290,7 +290,7 @@ pub fn try_normalize_use_tree_mut( fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { let use_tree_list = use_tree.use_tree_list()?; let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| { - let subtree_is_only_self = single_subtree.path().as_ref().map_or(false, path_is_self); + let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); let merged_path = match (use_tree.path(), single_subtree.path()) { // If the subtree is `{self}` then we cannot merge: `use diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 1f77ea1ec66..b3105e6524d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -320,7 +320,7 @@ impl<'a> Ranker<'a> { let same_text = tok.text() == self.text; // anything that mapped into a token tree has likely no semantic information let no_tt_parent = - tok.parent().map_or(false, |it| it.kind() != parser::SyntaxKind::TOKEN_TREE); + tok.parent().is_some_and(|it| it.kind() != parser::SyntaxKind::TOKEN_TREE); (both_idents as usize) | ((exact_same_kind as usize) << 1) | ((same_text as usize) << 2) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 156f21b784e..a045c22c2df 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -285,7 +285,7 @@ impl Ctx<'_> { if path.qualifier().is_some() { return None; } - if path.segment().map_or(false, |s| { + if path.segment().is_some_and(|s| { s.parenthesized_arg_list().is_some() || (s.self_token().is_some() && path.parent_path().is_none()) }) { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index c5215eb3e63..68199dd8711 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1081,7 +1081,7 @@ impl<'a> FindUsages<'a> { }; match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(Definition::SelfType(impl_))) + Some(NameRefClass::Definition(Definition::SelfType(impl_), _)) if ty_eq(impl_.self_ty(self.sema.db)) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); @@ -1102,7 +1102,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => { + Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let category = if is_name_ref_in_import(name_ref) { ReferenceCategory::IMPORT @@ -1147,7 +1147,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify_lifetime(self.sema, lifetime) { - Some(NameRefClass::Definition(def)) if def == self.def => { + Some(NameRefClass::Definition(def, _)) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); let reference = FileReference { range, @@ -1166,7 +1166,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(def)) + Some(NameRefClass::Definition(def, _)) if self.def == def // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait || matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_))) @@ -1182,7 +1182,7 @@ impl<'a> FindUsages<'a> { } // FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions // so we always resolve all assoc type aliases to both their trait def and impl defs - Some(NameRefClass::Definition(def)) + Some(NameRefClass::Definition(def, _)) if self.assoc_item_container.is_some() && matches!(self.def, Definition::TypeAlias(_)) && convert_to_def_in_trait(self.sema.db, def) @@ -1196,7 +1196,7 @@ impl<'a> FindUsages<'a> { }; sink(file_id, reference) } - Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => { + Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => { if self.include_self_kw_refs == def_to_ty(self.sema, &def) { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { @@ -1209,7 +1209,11 @@ impl<'a> FindUsages<'a> { false } } - Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { + Some(NameRefClass::FieldShorthand { + local_ref: local, + field_ref: field, + adt_subst: _, + }) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let field = Definition::Field(field); @@ -1240,7 +1244,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameClass::classify(self.sema, name) { - Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) + Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ }) if matches!( self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def ) => @@ -1352,12 +1356,12 @@ fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool { .parent() .and_then(ast::PathSegment::cast) .and_then(|it| it.parent_path().top_path().syntax().parent()) - .map_or(false, |it| it.kind() == SyntaxKind::USE_TREE) + .is_some_and(|it| it.kind() == SyntaxKind::USE_TREE) } fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool { name_ref.syntax().ancestors().any(|node| match ast::Fn::cast(node) { - Some(it) => sema.to_def(&it).map_or(false, |func| func.is_test(sema.db)), + Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), None => false, }) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 91e0b4495f5..74c0b8e2baa 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -220,7 +220,7 @@ pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool { match (this.kind(), other.kind()) { (VisibilityKind::In(this), VisibilityKind::In(other)) => { stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| { - lhs.kind().zip(rhs.kind()).map_or(false, |it| match it { + lhs.kind().zip(rhs.kind()).is_some_and(|it| match it { (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw) | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw) | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true, @@ -259,7 +259,7 @@ pub fn is_pattern_cond(expr: ast::Expr) -> bool { .or_else(|| expr.rhs().map(is_pattern_cond)) .unwrap_or(false) } - ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond), + ast::Expr::ParenExpr(expr) => expr.expr().is_some_and(is_pattern_cond), ast::Expr::LetExpr(_) => true, _ => false, } @@ -408,7 +408,7 @@ fn for_each_break_expr( } pub fn eq_label_lt(lt1: &Option<ast::Lifetime>, lt2: &Option<ast::Lifetime>) -> bool { - lt1.as_ref().zip(lt2.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text()) + lt1.as_ref().zip(lt2.as_ref()).is_some_and(|(lt, lbl)| lt.text() == lbl.text()) } struct TreeWithDepthIterator { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 1e08e8e3098..365d726d2a9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -2,13 +2,13 @@ use std::{collections::hash_map::Entry, str::FromStr}; -use hir::Semantics; +use hir::{Semantics, SemanticsScope}; use itertools::Itertools; use rustc_hash::FxHashMap; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, + match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, ToSmolStr, }; use crate::RootDatabase; @@ -100,6 +100,19 @@ impl NameGenerator { generator } + pub fn new_from_scope_locals(scope: Option<SemanticsScope<'_>>) -> Self { + let mut generator = Self::new(); + if let Some(scope) = scope { + scope.process_all_names(&mut |name, scope| { + if let hir::ScopeDef::Local(_) = scope { + generator.insert(name.as_str()); + } + }); + } + + generator + } + /// Suggest a name without conflicts. If the name conflicts with existing names, /// it will try to resolve the conflict by adding a numeric suffix. pub fn suggest_name(&mut self, name: &str) -> SmolStr { @@ -162,6 +175,59 @@ impl NameGenerator { self.suggest_name(&c.to_string()) } + /// Suggest name of variable for given expression + /// + /// In current implementation, the function tries to get the name from + /// the following sources: + /// + /// * if expr is an argument to function/method, use parameter name + /// * if expr is a function/method call, use function name + /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) + /// * fallback: `var_name` + /// + /// It also applies heuristics to filter out less informative names + /// + /// Currently it sticks to the first name found. + pub fn for_variable( + &mut self, + expr: &ast::Expr, + sema: &Semantics<'_, RootDatabase>, + ) -> SmolStr { + // `from_param` does not benefit from stripping it need the largest + // context possible so we check firstmost + if let Some(name) = from_param(expr, sema) { + return self.suggest_name(&name); + } + + let mut next_expr = Some(expr.clone()); + while let Some(expr) = next_expr { + let name = from_call(&expr) + .or_else(|| from_type(&expr, sema)) + .or_else(|| from_field_name(&expr)); + if let Some(name) = name { + return self.suggest_name(&name); + } + + match expr { + ast::Expr::RefExpr(inner) => next_expr = inner.expr(), + ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), + // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), + ast::Expr::CastExpr(inner) => next_expr = inner.expr(), + ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => { + next_expr = method.receiver(); + } + ast::Expr::ParenExpr(inner) => next_expr = inner.expr(), + ast::Expr::TryExpr(inner) => next_expr = inner.expr(), + ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => { + next_expr = prefix.expr() + } + _ => break, + } + } + + self.suggest_name("var_name") + } + /// Insert a name into the pool fn insert(&mut self, name: &str) { let (prefix, suffix) = Self::split_numeric_suffix(name); @@ -191,63 +257,8 @@ impl NameGenerator { } } -/// Suggest name of variable for given expression -/// -/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. -/// I.e. it doesn't look for names in scope. -/// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from -/// the following sources: -/// -/// * if expr is an argument to function/method, use parameter name -/// * if expr is a function/method call, use function name -/// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) -/// * fallback: `var_name` -/// -/// It also applies heuristics to filter out less informative names -/// -/// Currently it sticks to the first name found. -// FIXME: Microoptimize and return a `SmolStr` here. -pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { - // `from_param` does not benefit from stripping - // it need the largest context possible - // so we check firstmost - if let Some(name) = from_param(expr, sema) { - return name; - } - - let mut next_expr = Some(expr.clone()); - while let Some(expr) = next_expr { - let name = - from_call(&expr).or_else(|| from_type(&expr, sema)).or_else(|| from_field_name(&expr)); - if let Some(name) = name { - return name; - } - - match expr { - ast::Expr::RefExpr(inner) => next_expr = inner.expr(), - ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), - // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), - ast::Expr::CastExpr(inner) => next_expr = inner.expr(), - ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => { - next_expr = method.receiver(); - } - ast::Expr::ParenExpr(inner) => next_expr = inner.expr(), - ast::Expr::TryExpr(inner) => next_expr = inner.expr(), - ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => { - next_expr = prefix.expr() - } - _ => break, - } - } - - "var_name".to_owned() -} - -fn normalize(name: &str) -> Option<String> { - let name = to_lower_snake_case(name); +fn normalize(name: &str) -> Option<SmolStr> { + let name = to_lower_snake_case(name).to_smolstr(); if USELESS_NAMES.contains(&name.as_str()) { return None; @@ -280,11 +291,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool { } } -fn from_call(expr: &ast::Expr) -> Option<String> { +fn from_call(expr: &ast::Expr) -> Option<SmolStr> { from_func_call(expr).or_else(|| from_method_call(expr)) } -fn from_func_call(expr: &ast::Expr) -> Option<String> { +fn from_func_call(expr: &ast::Expr) -> Option<SmolStr> { let call = match expr { ast::Expr::CallExpr(call) => call, _ => return None, @@ -297,7 +308,7 @@ fn from_func_call(expr: &ast::Expr) -> Option<String> { normalize(ident.text()) } -fn from_method_call(expr: &ast::Expr) -> Option<String> { +fn from_method_call(expr: &ast::Expr) -> Option<SmolStr> { let method = match expr { ast::Expr::MethodCallExpr(call) => call, _ => return None, @@ -319,7 +330,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> { normalize(name) } -fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { +fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> { let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; let args_parent = arg_list.syntax().parent()?; let func = match_ast! { @@ -338,7 +349,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St let param = func.params().into_iter().nth(idx)?; let pat = sema.source(param)?.value.right()?.pat()?; let name = var_name_from_pat(&pat)?; - normalize(&name.to_string()) + normalize(&name.to_smolstr()) } fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> { @@ -350,7 +361,7 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> { } } -fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { +fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> { let ty = sema.type_of_expr(expr)?.adjusted(); let ty = ty.remove_ref().unwrap_or(ty); let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); @@ -358,7 +369,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<Str name_of_type(&ty, sema.db, edition) } -fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> { +fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<SmolStr> { let name = if let Some(adt) = ty.as_adt() { let name = adt.name(db).display(db, edition).to_string(); @@ -393,7 +404,7 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio Some(name) } -fn from_field_name(expr: &ast::Expr) -> Option<String> { +fn from_field_name(expr: &ast::Expr) -> Option<SmolStr> { let field = match expr { ast::Expr::FieldExpr(field) => field, _ => return None, @@ -424,7 +435,7 @@ mod tests { frange.range, "selection is not an expression(yet contained in one)" ); - let name = for_variable(&expr, &sema); + let name = NameGenerator::new().for_variable(&expr, &sema); assert_eq!(&name, expected); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index a319a0bcf6d..2b59c1a22f6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -34,6 +34,8 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String { return "you can specify generic arguments on either the enum or the variant, but not both" .to_owned(); } + GenericArgsProhibitedReason::Const => "constants", + GenericArgsProhibitedReason::Static => "statics", }; format!("generic arguments are not allowed on {kind}") } @@ -439,4 +441,167 @@ impl Trait for () { "#, ); } + + #[test] + fn in_record_expr() { + check_diagnostics( + r#" +mod foo { + pub struct Bar { pub field: i32 } +} +fn baz() { + let _ = foo::<()>::Bar { field: 0 }; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_record_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar { field: i32 } +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar { .. } = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_tuple_struct_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar(i32); +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar(..) = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_path_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar; +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_path_expr() { + check_diagnostics( + r#" +mod foo { + pub struct Bar; +} +fn baz() { + let _ = foo::<()>::Bar; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn const_and_static() { + check_diagnostics( + r#" +const CONST: i32 = 0; +static STATIC: i32 = 0; +fn baz() { + let _ = CONST::<()>; + // ^^^^^^ 💡 error: generic arguments are not allowed on constants + let _ = STATIC::<()>; + // ^^^^^^ 💡 error: generic arguments are not allowed on statics +} + "#, + ); + } + + #[test] + fn enum_variant() { + check_diagnostics( + r#" +enum Enum<A> { + Variant(A), +} +mod enum_ { + pub(super) use super::Enum::Variant as V; +} +fn baz() { + let v = Enum::<()>::Variant::<()>(()); + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both + let Enum::<()>::Variant::<()>(..) = v; + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both + let _ = Enum::<()>::Variant(()); + let _ = Enum::Variant::<()>(()); +} +fn foo() { + use Enum::Variant; + let _ = Variant::<()>(()); + let _ = enum_::V::<()>(()); + let _ = enum_::<()>::V::<()>(()); + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn dyn_trait() { + check_diagnostics( + r#" +mod foo { + pub trait Trait {} +} + +fn bar() { + let _: &dyn foo::<()>::Trait; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + let _: &foo::<()>::Trait; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn regression_18768() { + check_diagnostics( + r#" +//- minicore: result +//- /foo.rs crate:foo edition:2018 +pub mod lib { + mod core { + pub use core::*; + } + pub use self::core::result; +} + +pub mod __private { + pub use crate::lib::result::Result::{self, Err, Ok}; +} + +//- /bar.rs crate:bar deps:foo edition:2018 +fn bar() { + _ = foo::__private::Result::<(), ()>::Ok; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 5f38d13570a..8117401a534 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -237,6 +237,24 @@ fn main() { fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( r#" +#[rustc_intrinsic] +pub fn bitreverse(x: u32) -> u32; // Safe intrinsic +#[rustc_intrinsic] +pub unsafe fn floorf32(x: f32) -> f32; // Unsafe intrinsic + +fn main() { + let _ = bitreverse(12); + let _ = floorf32(12.0); + //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} +"#, + ); + } + + #[test] + fn no_missing_unsafe_diagnostic_with_legacy_safe_intrinsic() { + check_diagnostics( + r#" extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn bitreverse(x: u32) -> u32; // Safe intrinsic diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 121a463c9f1..6a4e5ba290e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -316,6 +316,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -401,6 +406,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -537,6 +547,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -597,6 +612,11 @@ fn main() {} }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -709,6 +729,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -746,6 +771,20 @@ mod tests { [ Annotation { range: 3..7, + kind: HasReferences { + pos: FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, + Annotation { + range: 3..7, kind: Runnable( Runnable { use_name_in_title: false, @@ -760,24 +799,15 @@ mod tests { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, Annotation { - range: 3..7, - kind: HasReferences { - pos: FilePositionWrapper { - file_id: FileId( - 0, - ), - offset: 3, - }, - data: Some( - [], - ), - }, - }, - Annotation { range: 18..23, kind: Runnable( Runnable { @@ -796,6 +826,11 @@ mod tests { path: "tests", }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -822,6 +857,11 @@ mod tests { }, }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index e5b4ed17b2a..8066894cd83 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -47,7 +47,7 @@ pub(crate) fn incoming_calls( .find_nodes_at_offset_with_descend(file, offset) .filter_map(move |node| match node { ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def @ Definition::Function(_)) => Some(def), + NameRefClass::Definition(def @ Definition::Function(_), _) => Some(def), _ => None, }, ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index ea16a11d56d..72fcac54177 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -147,8 +147,8 @@ pub(crate) fn external_docs( let definition = match_ast! { match node { ast::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref: _, field_ref } => { + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { local_ref: _, field_ref, adt_subst: _ } => { Definition::Field(field_ref) } NameRefClass::ExternCrateShorthand { decl, .. } => { @@ -157,7 +157,7 @@ pub(crate) fn external_docs( }, ast::Name(name) => match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref), + NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => Definition::Field(field_ref), }, _ => return None } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs index ebdd4add177..6cc240d6524 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs @@ -25,7 +25,7 @@ pub(super) fn parse_intra_doc_link(s: &str) -> (&str, Option<hir::Namespace>) { .find_map(|(ns, (prefixes, suffixes))| { if let Some(prefix) = prefixes.iter().find(|&&prefix| { s.starts_with(prefix) - && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ') + && s.chars().nth(prefix.len()).is_some_and(|c| c == '@' || c == ' ') }) { Some((&s[prefix.len() + 1..], ns)) } else { @@ -41,7 +41,7 @@ pub(super) fn strip_prefixes_suffixes(s: &str) -> &str { .find_map(|(prefixes, suffixes)| { if let Some(prefix) = prefixes.iter().find(|&&prefix| { s.starts_with(prefix) - && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ') + && s.chars().nth(prefix.len()).is_some_and(|c| c == '@' || c == ' ') }) { Some(&s[prefix.len() + 1..]) } else { diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 10a73edd51c..e028c5ff0cb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -253,6 +253,7 @@ fn _format( let &crate_id = db.relevant_crates(file_id).iter().next()?; let edition = db.crate_graph()[crate_id].edition; + #[allow(clippy::disallowed_methods)] let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path()); cmd.arg("--edition"); cmd.arg(edition.to_string()); @@ -573,7 +574,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl <>core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -599,7 +600,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl <>core::marker::Copy for Foo< >where{}"#]], ); } @@ -614,7 +615,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl <>core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -625,7 +626,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl <>core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 9dacbd8badf..7b6a5ef13e5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -36,7 +36,7 @@ pub(crate) fn goto_declaration( let def = match_ast! { match parent { ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { - NameRefClass::Definition(it) => Some(it), + NameRefClass::Definition(it, _) => Some(it), NameRefClass::FieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db), NameRefClass::ExternCrateShorthand { decl, .. } => diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 363f852e0e4..6c66907ec3e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -103,7 +103,7 @@ pub(crate) fn goto_definition( IdentClass::classify_node(sema, &parent)? .definitions() .into_iter() - .flat_map(|def| { + .flat_map(|(def, _)| { if let Definition::ExternCrateDecl(crate_def) = def { return crate_def .resolved_crate(db) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e36c8ee2f3f..04da1f67e95 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -48,7 +48,7 @@ pub(crate) fn goto_implementation( } ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref) .and_then(|class| match class { - NameRefClass::Definition(def) => Some(def), + NameRefClass::Definition(def, _) => Some(def), NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => None, }), diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 4690416e059..4002cbebad6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -307,7 +307,7 @@ fn hl_exit_points( let range = match &expr { ast::Expr::TryExpr(try_) => try_.question_mark_token().map(|token| token.text_range()), ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) - if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) => + if sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) => { Some(expr.syntax().text_range()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 332dfacbb43..1431bd8ca29 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -6,7 +6,7 @@ mod tests; use std::{iter, ops::Not}; use either::Either; -use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics}; +use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics}; use ide_db::{ defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, @@ -35,6 +35,14 @@ pub struct HoverConfig { pub max_trait_assoc_items_count: Option<usize>, pub max_fields_count: Option<usize>, pub max_enum_variants_count: Option<usize>, + pub max_subst_ty_len: SubstTyLen, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SubstTyLen { + Unlimited, + LimitTo(usize), + Hide, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -158,7 +166,8 @@ fn hover_offset( if let Some(doc_comment) = token_as_doc_comment(&original_token) { cov_mark::hit!(no_highlight_on_comment_hover); return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| { - let res = hover_for_definition(sema, file_id, def, &node, None, false, config, edition); + let res = + hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition); Some(RangeInfo::new(range, res)) }); } @@ -170,6 +179,7 @@ fn hover_offset( sema, file_id, Definition::from(resolution?), + None, &original_token.parent()?, None, false, @@ -217,7 +227,7 @@ fn hover_offset( { if let Some(macro_) = sema.resolve_macro_call(¯o_call) { break 'a vec![( - Definition::Macro(macro_), + (Definition::Macro(macro_), None), sema.resolve_macro_call_arm(¯o_call), false, node, @@ -236,7 +246,7 @@ fn hover_offset( decl, .. }) => { - vec![(Definition::ExternCrateDecl(decl), None, false, node)] + vec![((Definition::ExternCrateDecl(decl), None), None, false, node)] } class => { @@ -252,12 +262,13 @@ fn hover_offset( } } .into_iter() - .unique_by(|&(def, _, _, _)| def) - .map(|(def, macro_arm, hovered_definition, node)| { + .unique_by(|&((def, _), _, _, _)| def) + .map(|((def, subst), macro_arm, hovered_definition, node)| { hover_for_definition( sema, file_id, def, + subst, &node, macro_arm, hovered_definition, @@ -381,6 +392,7 @@ pub(crate) fn hover_for_definition( sema: &Semantics<'_, RootDatabase>, file_id: FileId, def: Definition, + subst: Option<GenericSubstitution>, scope_node: &SyntaxNode, macro_arm: Option<u32>, hovered_definition: bool, @@ -408,6 +420,7 @@ pub(crate) fn hover_for_definition( _ => None, }; let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); + let subst_types = subst.map(|subst| subst.types(db)); let markup = render::definition( sema.db, @@ -416,6 +429,7 @@ pub(crate) fn hover_for_definition( ¬able_traits, macro_arm, hovered_definition, + subst_types.as_ref(), config, edition, ); @@ -425,7 +439,7 @@ pub(crate) fn hover_for_definition( show_fn_references_action(sema.db, def), show_implementations_action(sema.db, def), runnable_action(sema, def, file_id), - goto_type_action_for_def(sema.db, def, ¬able_traits, edition), + goto_type_action_for_def(sema.db, def, ¬able_traits, subst_types, edition), ] .into_iter() .flatten() @@ -517,6 +531,7 @@ fn goto_type_action_for_def( db: &RootDatabase, def: Definition, notable_traits: &[(hir::Trait, Vec<(Option<hir::Type>, hir::Name)>)], + subst_types: Option<Vec<(hir::Symbol, hir::Type)>>, edition: Edition, ) -> Option<HoverAction> { let mut targets: Vec<hir::ModuleDef> = Vec::new(); @@ -554,6 +569,12 @@ fn goto_type_action_for_def( walk_and_push_ty(db, &ty, &mut push_new_def); } + if let Some(subst_types) = subst_types { + for (_, ty) in subst_types { + walk_and_push_ty(db, &ty, &mut push_new_def); + } + } + HoverAction::goto_type_from_targets(db, targets, edition) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index e617d462ecd..8fbd445d962 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -5,7 +5,7 @@ use either::Either; use hir::{ db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind, DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError, - MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo, + MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef, }; use ide_db::{ base_db::SourceDatabase, @@ -27,7 +27,7 @@ use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T} use crate::{ doc_links::{remove_links, rewrite_links}, - hover::{notable_traits, walk_and_push_ty}, + hover::{notable_traits, walk_and_push_ty, SubstTyLen}, interpret::render_const_eval_error, HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, @@ -274,7 +274,7 @@ pub(super) fn keyword( let markup = process_markup( sema.db, Definition::Module(doc_owner), - &markup(Some(docs.into()), description, None, None), + &markup(Some(docs.into()), description, None, None, String::new()), config, ); Some(HoverResult { markup, actions }) @@ -336,8 +336,8 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<Hove .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) .filter(|t| t.kind() == T![:]) .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) - .map_or(false, |t| { - t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy") + .is_some_and(|t| { + t.kind() == T![ident] && t.into_token().is_some_and(|t| t.text() == "clippy") }); if is_clippy { (true, CLIPPY_LINTS) @@ -378,7 +378,18 @@ pub(super) fn process_markup( fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> { match def { - Definition::Field(f) => Some(f.parent_def(db).name(db)), + Definition::Field(f) => { + let parent = f.parent_def(db); + let parent_name = parent.name(db); + let parent_name = parent_name.display(db, edition).to_string(); + return match parent { + VariantDef::Variant(variant) => { + let enum_name = variant.parent_enum(db).name(db); + Some(format!("{}::{parent_name}", enum_name.display(db, edition))) + } + _ => Some(parent_name), + }; + } Definition::Local(l) => l.parent(db).name(db), Definition::Variant(e) => Some(e.parent_enum(db).name(db)), @@ -421,6 +432,7 @@ pub(super) fn definition( notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)], macro_arm: Option<u32>, hovered_definition: bool, + subst_types: Option<&Vec<(Symbol, Type)>>, config: &HoverConfig, edition: Edition, ) -> Markup { @@ -582,12 +594,21 @@ pub(super) fn definition( _ => None, }; + let variance_info = || match def { + Definition::GenericParam(it) => it.variance(db).as_ref().map(ToString::to_string), + _ => None, + }; + let mut extra = String::new(); if hovered_definition { if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition) { extra.push_str("\n___\n"); extra.push_str(¬able_traits); } + if let Some(variance_info) = variance_info() { + extra.push_str("\n___\n"); + extra.push_str(&variance_info); + } if let Some(layout_info) = layout_info() { extra.push_str("\n___\n"); extra.push_str(&layout_info); @@ -604,7 +625,38 @@ pub(super) fn definition( desc.push_str(&value); } - markup(docs.map(Into::into), desc, extra.is_empty().not().then_some(extra), mod_path) + let subst_types = match config.max_subst_ty_len { + SubstTyLen::Hide => String::new(), + SubstTyLen::LimitTo(_) | SubstTyLen::Unlimited => { + let limit = if let SubstTyLen::LimitTo(limit) = config.max_subst_ty_len { + Some(limit) + } else { + None + }; + subst_types + .map(|subst_type| { + subst_type + .iter() + .filter(|(_, ty)| !ty.is_unknown()) + .format_with(", ", |(name, ty), fmt| { + fmt(&format_args!( + "`{name}` = `{}`", + ty.display_truncated(db, limit, edition) + )) + }) + .to_string() + }) + .unwrap_or_default() + } + }; + + markup( + docs.map(Into::into), + desc, + extra.is_empty().not().then_some(extra), + mod_path, + subst_types, + ) } pub(super) fn literal( @@ -663,10 +715,22 @@ pub(super) fn literal( let mut s = format!("```rust\n{ty}\n```\n___\n\n"); match value { Ok(value) => { + let backtick_len = value.chars().filter(|c| *c == '`').count(); + let spaces_len = value.chars().filter(|c| *c == ' ').count(); + let backticks = "`".repeat(backtick_len + 1); + let space_char = if spaces_len == value.len() { "" } else { " " }; + if let Some(newline) = value.find('\n') { - format_to!(s, "value of literal (truncated up to newline): {}", &value[..newline]) + format_to!( + s, + "value of literal (truncated up to newline): {backticks}{space_char}{}{space_char}{backticks}", + &value[..newline] + ) } else { - format_to!(s, "value of literal: {value}") + format_to!( + s, + "value of literal: {backticks}{space_char}{value}{space_char}{backticks}" + ) } } Err(error) => format_to!(s, "invalid literal: {error}"), @@ -831,12 +895,11 @@ fn closure_ty( } else { String::new() }; - let mut markup = format!("```rust\n{}", c.display_with_id(sema.db, edition)); + let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, edition)); if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { push_new_def(hir::Trait::from(trait_).into()) } - format_to!(markup, "\n{}\n```", c.display_with_impl(sema.db, edition),); if let Some(layout) = render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) { @@ -872,6 +935,7 @@ fn markup( rust: String, extra: Option<String>, mod_path: Option<String>, + subst_types: String, ) -> Markup { let mut buf = String::new(); @@ -886,6 +950,10 @@ fn markup( buf.push_str(&extra); } + if !subst_types.is_empty() { + format_to!(buf, "\n___\n{subst_types}"); + } + if let Some(doc) = docs { format_to!(buf, "\n___\n\n{}", doc); } @@ -901,7 +969,7 @@ fn find_std_module( let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(); std_root_module.children(db).find(|module| { - module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name) + module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 50d0d4c5df6..2e7637e4677 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -20,6 +20,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { max_trait_assoc_items_count: None, max_fields_count: Some(5), max_enum_variants_count: Some(5), + max_subst_ty_len: super::SubstTyLen::Unlimited, }; fn check_hover_no_result(ra_fixture: &str) { @@ -347,7 +348,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` ___ @@ -371,7 +371,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` ___ @@ -406,7 +405,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl FnOnce() ``` ___ @@ -436,7 +434,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl FnMut() ``` ___ @@ -462,7 +459,6 @@ fn main() { "#, expect![[r#" ```rust - {closure#0} impl FnOnce() -> S2 ``` ___ @@ -2322,6 +2318,53 @@ fn foo(Foo { b$0ar }: &Foo) {} } #[test] +fn test_hover_show_type_def_for_subst() { + check_actions( + r#" +fn f<T>(t: T) { + +} + +struct S; + +fn test() { + let a = S; + f$0(a); +} +"#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + ), + GoToType( + [ + HoverGotoTypeData { + mod_path: "ra_test_fixture::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 20..29, + focus_range: 27..28, + name: "S", + kind: Struct, + description: "struct S", + }, + }, + ], + ), + ] + "#]], + ); +} + +#[test] fn test_hover_non_ascii_space_doc() { check( " @@ -2969,7 +3012,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` @@ -3212,6 +3254,11 @@ fn foo_$0test() {} }, }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), ] @@ -3229,28 +3276,33 @@ mod tests$0 { } "#, expect![[r#" - [ - Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..46, - focus_range: 4..9, - name: "tests", - kind: Module, - description: "mod tests", - }, - kind: TestMod { - path: "tests", - }, - cfg: None, + [ + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..46, + focus_range: 4..9, + name: "tests", + kind: Module, + description: "mod tests", }, - ), - ] - "#]], + kind: TestMod { + path: "tests", + }, + cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, + }, + ), + ] + "#]], ); } @@ -4720,7 +4772,7 @@ fn hover_type_param_sized_bounds() { //- minicore: sized trait Trait {} struct Foo<T>(T); -impl<T: Trait> Foo<T$0> {} +impl<T$0: Trait> Foo<T> {} "#, expect![[r#" *T* @@ -4735,7 +4787,7 @@ impl<T: Trait> Foo<T$0> {} //- minicore: sized trait Trait {} struct Foo<T>(T); -impl<T: Trait + ?Sized> Foo<T$0> {} +impl<T$0: Trait + ?Sized> Foo<T> {} "#, expect![[r#" *T* @@ -4763,6 +4815,10 @@ fn foo<T$0>() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4780,6 +4836,10 @@ fn foo<T$0: Sized>() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4797,6 +4857,10 @@ fn foo<T$0: ?Sized>() {} ```rust T: ?Sized ``` + + --- + + invariant "#]], ); } @@ -4815,6 +4879,10 @@ fn foo<T$0: Trait>() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -4833,6 +4901,10 @@ fn foo<T$0: Trait + Sized>() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -4851,6 +4923,10 @@ fn foo<T$0: Trait + ?Sized>() {} ```rust T: Trait + ?Sized ``` + + --- + + invariant "#]], ); } @@ -4868,6 +4944,10 @@ fn foo<T$0: ?Sized + Sized + Sized>() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4886,6 +4966,10 @@ fn foo<T$0: Sized + ?Sized + Sized + Trait>() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -5176,6 +5260,10 @@ fn main() { --- + `Self` = `()` + + --- + false "#]], ); @@ -5208,6 +5296,10 @@ fn main() { --- + `Self` = `i32` + + --- + false "#]], ); @@ -7285,7 +7377,7 @@ enum Enum { *field* ```rust - ra_test_fixture::RecordV + ra_test_fixture::Enum::RecordV ``` ```rust @@ -8116,7 +8208,7 @@ fn main() { ``` ___ - value of literal: 🦀🦀\A + value of literal: ` 🦀🦀\A ` "#]], ); check( @@ -8132,7 +8224,7 @@ fn main() { ``` ___ - value of literal: 🦀\u{1f980}\\\x41 + value of literal: ` 🦀\u{1f980}\\\x41 ` "#]], ); check( @@ -8154,7 +8246,7 @@ fsdghs"; ``` ___ - value of literal (truncated up to newline): 🦀\u{1f980}\\\x41 + value of literal (truncated up to newline): ` 🦀\u{1f980}\\\x41 ` "#]], ); } @@ -8174,9 +8266,74 @@ fn main() { ``` ___ - value of literal: 🦀🦀\A + value of literal: ` 🦀🦀\A ` + "#]], + ); +} + +#[test] +fn rawstring_literal() { + check( + r#" +fn main() { + $0r"`[^`]*`"; +}"#, + expect![[r#" + *r"`[^`]*`"* + ```rust + &str + ``` + ___ + + value of literal: ```` `[^`]*` ```` + "#]], + ); + check( + r#" +fn main() { + $0r"`"; +}"#, + expect![[r#" + *r"`"* + ```rust + &str + ``` + ___ + + value of literal: `` ` `` + "#]], + ); + check( + r#" +fn main() { + $0r" "; +}"#, + expect![[r#" + *r" "* + ```rust + &str + ``` + ___ + + value of literal: ` ` "#]], ); + check( + r#" +fn main() { + $0r" Hello World "; + +}"#, + expect![[r#" + *r" Hello World "* + ```rust + &str + ``` + ___ + + value of literal: ` Hello World ` +"#]], + ) } #[test] @@ -8194,7 +8351,7 @@ fn main() { ``` ___ - value of literal: [240, 159, 166, 128, 92] + value of literal: ` [240, 159, 166, 128, 92] ` "#]], ); check( @@ -8210,7 +8367,7 @@ fn main() { ``` ___ - value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] + value of literal: ` [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] ` "#]], ); } @@ -8230,7 +8387,7 @@ fn main() { ``` ___ - value of literal: 0xF0 + value of literal: ` 0xF0 ` "#]], ); check( @@ -8246,7 +8403,7 @@ fn main() { ``` ___ - value of literal: 0x5C + value of literal: ` 0x5C ` "#]], ); } @@ -8266,7 +8423,7 @@ fn main() { ``` ___ - value of literal: A + value of literal: ` A ` "#]], ); check( @@ -8282,7 +8439,7 @@ fn main() { ``` ___ - value of literal: \ + value of literal: ` \ ` "#]], ); check( @@ -8298,7 +8455,7 @@ fn main() { ``` ___ - value of literal: 🦀 + value of literal: ` 🦀 ` "#]], ); } @@ -8318,7 +8475,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3FF0000000000000) + value of literal: ` 1 (bits: 0x3FF0000000000000) ` "#]], ); check( @@ -8334,7 +8491,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3C00) + value of literal: ` 1 (bits: 0x3C00) ` "#]], ); check( @@ -8350,7 +8507,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3F800000) + value of literal: ` 1 (bits: 0x3F800000) ` "#]], ); check( @@ -8366,7 +8523,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3FFF0000000000000000000000000000) + value of literal: ` 1 (bits: 0x3FFF0000000000000000000000000000) ` "#]], ); check( @@ -8382,7 +8539,7 @@ fn main() { ``` ___ - value of literal: 134000000000000 (bits: 0x42DE77D399980000) + value of literal: ` 134000000000000 (bits: 0x42DE77D399980000) ` "#]], ); check( @@ -8398,7 +8555,7 @@ fn main() { ``` ___ - value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B) + value of literal: ` 1523527134274733600000000 (bits: 0x44F429E9249F629B) ` "#]], ); check( @@ -8434,7 +8591,7 @@ fn main() { ``` ___ - value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) + value of literal: ` 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) ` "#]], ); check( @@ -8450,7 +8607,7 @@ fn main() { ``` ___ - value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) + value of literal: ` 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) ` "#]], ); check( @@ -8466,7 +8623,7 @@ fn main() { ``` ___ - value of literal: 306328611 (0x12423423|0b10010010000100011010000100011) + value of literal: ` 306328611 (0x12423423|0b10010010000100011010000100011) ` "#]], ); check( @@ -8482,7 +8639,7 @@ fn main() { ``` ___ - value of literal: 255 (0xFF|0b11111111) + value of literal: ` 255 (0xFF|0b11111111) ` "#]], ); check( @@ -8498,7 +8655,7 @@ fn main() { ``` ___ - value of literal: 5349 (0x14E5|0b1010011100101) + value of literal: ` 5349 (0x14E5|0b1010011100101) ` "#]], ); check( @@ -9501,3 +9658,539 @@ fn main() { "#]], ); } + +#[test] +fn subst_fn() { + check( + r#" +struct Foo<T>(T); +impl<T> Foo<T> { + fn foo<U>(v: T, u: U) {} +} + +fn bar() { + Foo::fo$0o(123, false); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl<T> Foo<T> + fn foo<U>(v: T, u: U) + ``` + + --- + + `T` = `i32`, `U` = `bool` + "#]], + ); + check( + r#" +fn foo<T>(v: T) {} + +fn bar() { + fo$0o(123); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture + ``` + + ```rust + fn foo<T>(v: T) + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn subst_record_constructor() { + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = $0Foo { field: 123 }; +} + "#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo<T> { + field: T, + } + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let $0Foo { field: _ } = v; +} + "#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo<T> { + field: T, + } + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn subst_method_call() { + check( + r#" +struct Foo<T>(T); + +impl<U> Foo<U> { + fn bar<T>(self, v: T) {} +} + +fn baz() { + Foo(123).bar$0("hello"); +} + "#, + expect![[r#" + *bar* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl<U> Foo<U> + fn bar<T>(self, v: T) + ``` + + --- + + `U` = `i32`, `T` = `&str` + "#]], + ); +} + +#[test] +fn subst_type_alias_do_not_work() { + // It is very hard to support subst for type aliases properly in all places because they are eagerly evaluated. + // We can show the user the subst for the underlying type instead but that'll be very confusing. + check( + r#" +struct Foo<T, U> { a: T, b: U } +type Alias<T> = Foo<T, i32>; + +fn foo() { + let _ = Alias$0 { a: true, b: 123 }; +} + "#, + expect![[r#" + *Alias* + + ```rust + ra_test_fixture + ``` + + ```rust + type Alias<T> = Foo<T, i32> + ``` + "#]], + ); +} + +#[test] +fn subst_self() { + check( + r#" +trait Trait<T> { + fn foo<U>(&self, v: U) {} +} +struct Struct<T>(T); +impl<T> Trait<i64> for Struct<T> {} + +fn bar() { + Struct(123).foo$0(true); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Trait + ``` + + ```rust + trait Trait<T> + fn foo<U>(&self, v: U) + ``` + + --- + + `Self` = `Struct<i32>`, `T` = `i64`, `U` = `bool` + "#]], + ); +} + +#[test] +fn subst_with_lifetimes_and_consts() { + check( + r#" +struct Foo<'a, const N: usize, T>(&[T; N]); + +impl<'a, T, const N: usize> Foo<'a, N, T> { + fn foo<'b, const Z: u32, U>(&self, v: U) {} +} + +fn bar() { + Foo(&[1i8]).fo$0o::<456, _>(""); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl<'a, T, const N: usize> Foo<'a, N, T> + fn foo<'b, const Z: u32, U>(&self, v: U) + ``` + + --- + + `T` = `i8`, `U` = `&str` + "#]], + ); +} + +#[test] +fn subst_field() { + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = Foo { $0field: 123 }; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let field = 123; + let v = Foo { field$0 }; +} + "#, + expect![[r#" + *field* + + ```rust + let field: i32 + ``` + --- + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let Foo { field$0 } = v; +} + "#, + expect![[r#" + *field* + + ```rust + let field: i32 + ``` + + --- + + size = 4, align = 4 + --- + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let Foo { field$0: _ } = v; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T> { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let _ = (&v).$0field; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo<T>(T); + +fn bar() { + let v = Foo(123); + let _ = v.$00; +} + "#, + expect![[r#" + *0* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + 0: T + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn i128_max() { + check( + r#" +//- /core.rs library crate:core +#![rustc_coherence_is_core] +impl u128 { + pub const MAX: Self = 340_282_366_920_938_463_463_374_607_431_768_211_455u128; +} +impl i128 { + pub const MAX: Self = (u128::MAX >> 1) as Self; +} + +//- /foo.rs crate:foo deps:core +fn foo() { + let _ = i128::MAX$0; +} + "#, + expect![ + r#" + *MAX* + + ```rust + core + ``` + + ```rust + pub const MAX: Self = 170141183460469231731687303715884105727 (0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + ``` + "# + ], + ); +} + +#[test] +fn test_runnables_with_snapshot_tests() { + check_actions( + r#" +//- /lib.rs crate:foo deps:expect_test,insta,snapbox +use expect_test::expect; +use insta::assert_debug_snapshot; +use snapbox::Assert; + +#[test] +fn test$0() { + let actual = "new25"; + expect!["new25"].assert_eq(&actual); + Assert::new() + .action_env("SNAPSHOTS") + .eq(actual, snapbox::str!["new25"]); + assert_debug_snapshot!(actual); +} + +//- /lib.rs crate:expect_test +struct Expect; + +impl Expect { + fn assert_eq(&self, actual: &str) {} +} + +#[macro_export] +macro_rules! expect { + ($e:expr) => Expect; // dummy +} + +//- /lib.rs crate:insta +#[macro_export] +macro_rules! assert_debug_snapshot { + ($e:expr) => {}; // dummy +} + +//- /lib.rs crate:snapbox +pub struct Assert; + +impl Assert { + pub fn new() -> Self { Assert } + + pub fn action_env(&self, env: &str) -> &Self { self } + + pub fn eq(&self, actual: &str, expected: &str) {} +} + +#[macro_export] +macro_rules! str { + ($e:expr) => ""; // dummy +} + "#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 92, + }, + ), + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 81..301, + focus_range: 92..96, + name: "test", + kind: Function, + }, + kind: Test { + test_id: Path( + "test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + update_test: UpdateTest { + expect_test: true, + insta: true, + snapbox: true, + }, + }, + ), + ] + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index aa99ba49bc8..faa65019eea 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -856,4 +856,18 @@ fn main() { }"#, ); } + + #[test] + fn regression_18840() { + check( + r#" +//- proc_macros: issue_18840 +#[proc_macros::issue_18840] +fn foo() { + let + loop {} +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 4d7d6e270e0..4e48baa6f14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -313,7 +313,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) - let needs_outer_parens = parent.map_or(false, |p| dummy_expr.needs_parens_in(p)); + let needs_outer_parens = parent.is_some_and(|p| dummy_expr.needs_parens_in(p)); let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone()); (needs_outer_parens, needs_inner_parens) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index cfe8657fd05..5afb98cb1c7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -61,7 +61,6 @@ pub(super) fn hints( } hint.label.append_str(r); }); - hint.pad_right = was_mut_last; let acc_base = acc.len(); match pat { ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { @@ -86,6 +85,7 @@ pub(super) fn hints( } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { hint.label.append_str("("); + was_mut_last = false; acc.push(InlayHint::closing_paren_after( InlayKind::BindingMode, pat.syntax().text_range(), @@ -94,6 +94,7 @@ pub(super) fn hints( _ => (), } if !hint.label.parts.is_empty() { + hint.pad_right = was_mut_last; acc.push(hint); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index cd77c3ec3e9..8f2949cb387 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -32,7 +32,7 @@ pub(super) fn enum_hints( return None; } // data carrying enums without a primitive repr have no stable discriminants - if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) { + if data_carrying && def.repr(sema.db).is_none_or(|r| r.int.is_none()) { return None; } for variant in enum_.variant_list()?.variants() { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 28b0fa6dd4d..a03ff6a52b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -153,7 +153,7 @@ fn is_param_name_suffix_of_fn_name( .len() .checked_sub(param_name.len()) .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at))) - .map_or(false, |(prefix, suffix)| { + .is_some_and(|(prefix, suffix)| { suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_') }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index c13fc843568..6e7c718953c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -86,7 +86,7 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, - MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen, }, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, @@ -96,8 +96,8 @@ pub use crate::{ join_lines::JoinLinesConfig, markup::Markup, moniker::{ - MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation, - SymbolInformationKind, + Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerKind, MonikerResult, + PackageInformation, SymbolInformationKind, }, move_item::Direction, navigation_target::{NavigationTarget, TryToNav, UpmappingResult}, @@ -671,19 +671,17 @@ impl Analysis { /// Computes completions at the given position. pub fn completions( &self, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, trigger_character: Option<char>, ) -> Cancellable<Option<Vec<CompletionItem>>> { - self.with_db(|db| { - ide_completion::completions(db, config, position, trigger_character).map(Into::into) - }) + self.with_db(|db| ide_completion::completions(db, config, position, trigger_character)) } /// Resolves additional completion data at the position given. pub fn resolve_completion_edits( &self, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, imports: impl IntoIterator<Item = (String, String)> + std::panic::UnwindSafe, ) -> Cancellable<Vec<TextEdit>> { diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 14781b21296..052466725fa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -3,7 +3,7 @@ use core::fmt; -use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, MacroKind, Semantics}; +use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics}; use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin}, defs::{Definition, IdentClass}, @@ -11,6 +11,7 @@ use ide_db::{ FilePosition, RootDatabase, }; use itertools::Itertools; +use span::Edition; use syntax::{AstNode, SyntaxKind::*, T}; use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; @@ -57,8 +58,8 @@ pub enum SymbolInformationKind { impl From<SymbolInformationKind> for MonikerDescriptorKind { fn from(value: SymbolInformationKind) -> Self { match value { - SymbolInformationKind::AssociatedType => Self::TypeParameter, - SymbolInformationKind::Attribute => Self::Macro, + SymbolInformationKind::AssociatedType => Self::Type, + SymbolInformationKind::Attribute => Self::Meta, SymbolInformationKind::Constant => Self::Term, SymbolInformationKind::Enum => Self::Type, SymbolInformationKind::EnumMember => Self::Type, @@ -70,7 +71,7 @@ impl From<SymbolInformationKind> for MonikerDescriptorKind { SymbolInformationKind::Parameter => Self::Parameter, SymbolInformationKind::SelfParameter => Self::Parameter, SymbolInformationKind::StaticMethod => Self::Method, - SymbolInformationKind::StaticVariable => Self::Meta, + SymbolInformationKind::StaticVariable => Self::Term, SymbolInformationKind::Struct => Self::Type, SymbolInformationKind::Trait => Self::Type, SymbolInformationKind::TraitMethod => Self::Method, @@ -109,10 +110,12 @@ pub enum MonikerKind { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MonikerResult { - pub identifier: MonikerIdentifier, - pub kind: MonikerKind, - pub package_information: PackageInformation, +pub enum MonikerResult { + /// Uniquely identifies a definition. + Moniker(Moniker), + /// Specifies that the definition is a local, and so does not have a unique identifier. Provides + /// a unique identifier for the container. + Local { enclosing_moniker: Option<Moniker> }, } impl MonikerResult { @@ -121,6 +124,15 @@ impl MonikerResult { } } +/// Information which uniquely identifies a definition which might be referenceable outside of the +/// source file. Visibility declarations do not affect presence. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Moniker { + pub identifier: MonikerIdentifier, + pub kind: MonikerKind, + pub package_information: PackageInformation, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, @@ -232,157 +244,106 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati } } +/// Computes a `MonikerResult` for a definition. Result cases: +/// +/// * `Some(MonikerResult::Moniker(_))` provides a unique `Moniker` which refers to a definition. +/// +/// * `Some(MonikerResult::Local { .. })` provides a `Moniker` for the definition enclosing a local. +/// +/// * `None` is returned for definitions which are not in a module: `BuiltinAttr`, `BuiltinType`, +/// `BuiltinLifetime`, `TupleField`, `ToolModule`, and `InlineAsmRegOrRegClass`. TODO: it might be +/// sensible to provide monikers that refer to some non-existent crate of compiler builtin +/// definitions. pub(crate) fn def_to_moniker( db: &RootDatabase, - def: Definition, + definition: Definition, from_crate: Crate, ) -> Option<MonikerResult> { - if matches!( - def, - Definition::GenericParam(_) - | Definition::Label(_) - | Definition::DeriveHelper(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - ) { - return None; + match definition { + Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => { + return Some(MonikerResult::Local { + enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate), + }); + } + _ => {} } + Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?)) +} - let module = def.module(db)?; +fn enclosing_def_to_moniker( + db: &RootDatabase, + mut def: Definition, + from_crate: Crate, +) -> Option<Moniker> { + loop { + let enclosing_def = def.enclosing_definition(db)?; + if let Some(enclosing_moniker) = def_to_non_local_moniker(db, enclosing_def, from_crate) { + return Some(enclosing_moniker); + } + def = enclosing_def; + } +} + +fn def_to_non_local_moniker( + db: &RootDatabase, + definition: Definition, + from_crate: Crate, +) -> Option<Moniker> { + let module = definition.module(db)?; let krate = module.krate(); let edition = krate.edition(db); - let mut description = vec![]; - description.extend(module.path_to_root(db).into_iter().filter_map(|x| { - Some(MonikerDescriptor { - name: x.name(db)?.display(db, edition).to_string(), - desc: def_to_kind(db, x.into()).into(), - }) - })); - - // Handle associated items within a trait - if let Some(assoc) = def.as_assoc_item(db) { - let container = assoc.container(db); - match container { - AssocItemContainer::Trait(trait_) => { - // Because different traits can have functions with the same name, - // we have to include the trait name as part of the moniker for uniqueness. - description.push(MonikerDescriptor { - name: trait_.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, trait_.into()).into(), - }); - } - AssocItemContainer::Impl(impl_) => { - // Because a struct can implement multiple traits, for implementations - // we add both the struct name and the trait name to the path - if let Some(adt) = impl_.self_ty(db).as_adt() { - description.push(MonikerDescriptor { - name: adt.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, adt.into()).into(), - }); - } - if let Some(trait_) = impl_.trait_(db) { - description.push(MonikerDescriptor { - name: trait_.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, trait_.into()).into(), + // Add descriptors for this definition and every enclosing definition. + let mut reverse_description = vec![]; + let mut def = definition; + loop { + match def { + Definition::SelfType(impl_) => { + if let Some(trait_ref) = impl_.trait_ref(db) { + // Trait impls use the trait type for the 2nd parameter. + reverse_description.push(MonikerDescriptor { + name: display(db, edition, module, trait_ref), + desc: MonikerDescriptorKind::TypeParameter, }); } - } - } - } - - if let Definition::Field(it) = def { - description.push(MonikerDescriptor { - name: it.parent_def(db).name(db).display(db, edition).to_string(), - desc: def_to_kind(db, it.parent_def(db).into()).into(), - }); - } - - // Qualify locals/parameters by their parent definition name. - if let Definition::Local(it) = def { - let parent = Definition::try_from(it.parent(db)).ok(); - if let Some(parent) = parent { - let parent_name = parent.name(db); - if let Some(name) = parent_name { - description.push(MonikerDescriptor { - name: name.display(db, edition).to_string(), - desc: def_to_kind(db, parent).into(), + // Both inherent and trait impls use the self type for the first parameter. + reverse_description.push(MonikerDescriptor { + name: display(db, edition, module, impl_.self_ty(db)), + desc: MonikerDescriptorKind::TypeParameter, + }); + reverse_description.push(MonikerDescriptor { + name: "impl".to_owned(), + desc: MonikerDescriptorKind::Type, }); } - } - } - - let desc = def_to_kind(db, def).into(); - - let name_desc = match def { - // These are handled by top-level guard (for performance). - Definition::GenericParam(_) - | Definition::Label(_) - | Definition::DeriveHelper(_) - | Definition::BuiltinLifetime(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmOperand(_) => return None, - - Definition::Local(local) => { - if !local.is_param(db) { - return None; + _ => { + if let Some(name) = def.name(db) { + reverse_description.push(MonikerDescriptor { + name: name.display(db, edition).to_string(), + desc: def_to_kind(db, def).into(), + }); + } else if reverse_description.is_empty() { + // Don't allow the last descriptor to be absent. + return None; + } else { + match def { + Definition::Module(module) if module.is_crate_root() => {} + _ => { + tracing::error!(?def, "Encountered enclosing definition with no name"); + } + } + } } - - MonikerDescriptor { name: local.name(db).display(db, edition).to_string(), desc } } - Definition::Macro(m) => { - MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc } - } - Definition::Function(f) => { - MonikerDescriptor { name: f.name(db).display(db, edition).to_string(), desc } - } - Definition::Variant(v) => { - MonikerDescriptor { name: v.name(db).display(db, edition).to_string(), desc } - } - Definition::Const(c) => { - MonikerDescriptor { name: c.name(db)?.display(db, edition).to_string(), desc } - } - Definition::Trait(trait_) => { - MonikerDescriptor { name: trait_.name(db).display(db, edition).to_string(), desc } - } - Definition::TraitAlias(ta) => { - MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc } - } - Definition::TypeAlias(ta) => { - MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc } - } - Definition::Module(m) => { - MonikerDescriptor { name: m.name(db)?.display(db, edition).to_string(), desc } - } - Definition::BuiltinType(b) => { - MonikerDescriptor { name: b.name().display(db, edition).to_string(), desc } - } - Definition::SelfType(imp) => MonikerDescriptor { - name: imp.self_ty(db).as_adt()?.name(db).display(db, edition).to_string(), - desc, - }, - Definition::Field(it) => { - MonikerDescriptor { name: it.name(db).display(db, edition).to_string(), desc } - } - Definition::TupleField(it) => { - MonikerDescriptor { name: it.name().display(db, edition).to_string(), desc } - } - Definition::Adt(adt) => { - MonikerDescriptor { name: adt.name(db).display(db, edition).to_string(), desc } - } - Definition::Static(s) => { - MonikerDescriptor { name: s.name(db).display(db, edition).to_string(), desc } - } - Definition::ExternCrateDecl(m) => { - MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc } - } - }; - - description.push(name_desc); + let Some(next_def) = def.enclosing_definition(db) else { + break; + }; + def = next_def; + } + reverse_description.reverse(); + let description = reverse_description; - Some(MonikerResult { + Some(Moniker { identifier: MonikerIdentifier { crate_name: krate.display_name(db)?.crate_name().to_string(), description, @@ -417,17 +378,57 @@ pub(crate) fn def_to_moniker( }) } +fn display<T: HirDisplay>( + db: &RootDatabase, + edition: Edition, + module: hir::Module, + it: T, +) -> String { + match it.display_source_code(db, module.into(), true) { + Ok(result) => result, + // Fallback on display variant that always succeeds + Err(_) => { + let fallback_result = it.display(db, edition).to_string(); + tracing::error!( + display = %fallback_result, "`display_source_code` failed; falling back to using display" + ); + fallback_result + } + } +} + #[cfg(test)] mod tests { - use crate::fixture; + use crate::{fixture, MonikerResult}; use super::MonikerKind; + #[allow(dead_code)] #[track_caller] fn no_moniker(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); if let Some(x) = analysis.moniker(position).unwrap() { - assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {x:?}"); + assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}"); + } + } + + #[track_caller] + fn check_local_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) { + let (analysis, position) = fixture::position(ra_fixture); + let x = analysis.moniker(position).unwrap().expect("no moniker found").info; + assert_eq!(x.len(), 1); + match x.into_iter().next().unwrap() { + MonikerResult::Local { enclosing_moniker: Some(x) } => { + assert_eq!(identifier, x.identifier.to_string()); + assert_eq!(package, format!("{:?}", x.package_information)); + assert_eq!(kind, x.kind); + } + MonikerResult::Local { enclosing_moniker: None } => { + panic!("Unexpected local with no enclosing moniker"); + } + MonikerResult::Moniker(_) => { + panic!("Unexpected non-local moniker"); + } } } @@ -436,10 +437,16 @@ mod tests { let (analysis, position) = fixture::position(ra_fixture); let x = analysis.moniker(position).unwrap().expect("no moniker found").info; assert_eq!(x.len(), 1); - let x = x.into_iter().next().unwrap(); - assert_eq!(identifier, x.identifier.to_string()); - assert_eq!(package, format!("{:?}", x.package_information)); - assert_eq!(kind, x.kind); + match x.into_iter().next().unwrap() { + MonikerResult::Local { enclosing_moniker } => { + panic!("Unexpected local enclosed in {:?}", enclosing_moniker); + } + MonikerResult::Moniker(x) => { + assert_eq!(identifier, x.identifier.to_string()); + assert_eq!(package, format!("{:?}", x.package_information)); + assert_eq!(kind, x.kind); + } + } } #[test] @@ -538,15 +545,13 @@ pub mod module { pub trait MyTrait { pub fn func() {} } - struct MyStruct {} - impl MyTrait for MyStruct { pub fn func$0() {} } } "#, - "foo::module::MyStruct::MyTrait::func", + "foo::module::impl::MyStruct::MyTrait::func", r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); @@ -573,8 +578,8 @@ pub struct St { } #[test] - fn no_moniker_for_local() { - no_moniker( + fn local() { + check_local_moniker( r#" //- /lib.rs crate:main deps:foo use foo::module::func; @@ -588,6 +593,9 @@ pub mod module { } } "#, + "foo::module::func", + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, + MonikerKind::Export, ); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 74c50fcac35..b51a5cc4f4c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -34,7 +34,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na if let Some(m) = &module { if !m .item_list() - .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) + .is_some_and(|it| it.syntax().text_range().contains_inclusive(position.offset)) { cov_mark::hit!(test_resolve_parent_module_on_module_decl); module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 339315db571..46714df8d69 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -112,7 +112,7 @@ pub(crate) fn find_all_refs( Some(name) => { let def = match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def: _, field_ref } => { + NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => { Definition::Field(field_ref) } }; @@ -156,10 +156,12 @@ pub(crate) fn find_defs<'a>( let def = match name_like { ast::NameLike::NameRef(name_ref) => { match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref, field_ref: _ } => { - Definition::Local(local_ref) - } + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { + local_ref, + field_ref: _, + adt_subst: _, + } => Definition::Local(local_ref), NameRefClass::ExternCrateShorthand { decl, .. } => { Definition::ExternCrateDecl(decl) } @@ -167,14 +169,14 @@ pub(crate) fn find_defs<'a>( } ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }, ast::NameLike::Lifetime(lifetime) => { NameRefClass::classify_lifetime(sema, &lifetime) .and_then(|class| match class { - NameRefClass::Definition(it) => Some(it), + NameRefClass::Definition(it, _) => Some(it), _ => None, }) .or_else(|| { @@ -203,14 +205,14 @@ fn retain_adt_literal_usages( reference .name .as_name_ref() - .map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref)) + .is_some_and(|name_ref| is_enum_lit_name_ref(sema, enum_, name_ref)) }) }); usages.references.retain(|_, it| !it.is_empty()); } Definition::Adt(_) | Definition::Variant(_) => { refs.for_each(|it| { - it.retain(|reference| reference.name.as_name_ref().map_or(false, is_lit_name_ref)) + it.retain(|reference| reference.name.as_name_ref().is_some_and(is_lit_name_ref)) }); usages.references.retain(|_, it| !it.is_empty()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index a9519c03b32..11bbd99110b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -227,8 +227,7 @@ fn find_definitions( ast::NameLike::Name(name) if name .syntax() - .parent() - .map_or(false, |it| ast::Rename::can_cast(it.kind())) + .parent().is_some_and(|it| ast::Rename::can_cast(it.kind())) // FIXME: uncomment this once we resolve to usages to extern crate declarations // && name // .syntax() @@ -242,7 +241,7 @@ fn find_definitions( ast::NameLike::Name(name) => NameClass::classify(sema, name) .map(|class| match class { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }) @@ -250,8 +249,8 @@ fn find_definitions( ast::NameLike::NameRef(name_ref) => { NameRefClass::classify(sema, name_ref) .map(|class| match class { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref, field_ref: _ } => { + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ } => { Definition::Local(local_ref) } NameRefClass::ExternCrateShorthand { decl, .. } => { @@ -264,8 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db) - .map_or(false, |it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) { Err(format_err!("Renaming aliases is currently unsupported")) } else { @@ -276,7 +274,7 @@ fn find_definitions( ast::NameLike::Lifetime(lifetime) => { NameRefClass::classify_lifetime(sema, lifetime) .and_then(|class| match class { - NameRefClass::Definition(def) => Some(def), + NameRefClass::Definition(def, _) => Some(def), _ => None, }) .or_else(|| { diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index d385e453e21..3e39c750b13 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -1,10 +1,11 @@ -use std::fmt; +use std::{fmt, sync::OnceLock}; +use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt, - Semantics, + ModPath, Name, PathKind, Semantics, Symbol, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ @@ -15,11 +16,12 @@ use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; +use smallvec::SmallVec; use span::{Edition, TextSize}; use stdx::format_to; use syntax::{ ast::{self, AstNode}, - SmolStr, SyntaxNode, ToSmolStr, + format_smolstr, SmolStr, SyntaxNode, ToSmolStr, }; use crate::{references, FileId, NavigationTarget, ToNav, TryToNav}; @@ -30,6 +32,7 @@ pub struct Runnable { pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg: Option<CfgExpr>, + pub update_test: UpdateTest, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -334,14 +337,20 @@ pub(crate) fn runnable_fn( } }; + let fn_source = sema.source(def)?; let nav = NavigationTarget::from_named( sema.db, - def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName), + fn_source.as_ref().map(|it| it as &dyn ast::HasName), SymbolKind::Function, ) .call_site(); + + let file_range = fn_source.syntax().original_file_range_with_macro_call_body(sema.db); + let update_test = + UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range); + let cfg = def.attrs(sema.db).cfg(); - Some(Runnable { use_name_in_title: false, nav, kind, cfg }) + Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test }) } pub(crate) fn runnable_mod( @@ -366,7 +375,22 @@ pub(crate) fn runnable_mod( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site(); - Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg }) + + let module_source = sema.module_definition_node(def); + let module_syntax = module_source.file_syntax(sema.db); + let file_range = hir::FileRange { + file_id: module_source.file_id.original_file(sema.db), + range: module_syntax.text_range(), + }; + let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range); + + Some(Runnable { + use_name_in_title: false, + nav, + kind: RunnableKind::TestMod { path }, + cfg, + update_test, + }) } pub(crate) fn runnable_impl( @@ -392,7 +416,19 @@ pub(crate) fn runnable_impl( test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); - Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) + let impl_source = sema.source(*def)?; + let impl_syntax = impl_source.syntax(); + let file_range = impl_syntax.original_file_range_with_macro_call_body(sema.db); + let update_test = + UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range); + + Some(Runnable { + use_name_in_title: false, + nav, + kind: RunnableKind::DocTest { test_id }, + cfg, + update_test, + }) } fn has_cfg_test(attrs: AttrsWithOwner) -> bool { @@ -404,6 +440,8 @@ fn runnable_mod_outline_definition( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option<Runnable> { + def.as_source_file_id(sema.db)?; + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) { return None; @@ -421,16 +459,22 @@ fn runnable_mod_outline_definition( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - if def.as_source_file_id(sema.db).is_some() { - Some(Runnable { - use_name_in_title: false, - nav: def.to_nav(sema.db).call_site(), - kind: RunnableKind::TestMod { path }, - cfg, - }) - } else { - None - } + + let mod_source = sema.module_definition_node(def); + let mod_syntax = mod_source.file_syntax(sema.db); + let file_range = hir::FileRange { + file_id: mod_source.file_id.original_file(sema.db), + range: mod_syntax.text_range(), + }; + let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range); + + Some(Runnable { + use_name_in_title: false, + nav: def.to_nav(sema.db).call_site(), + kind: RunnableKind::TestMod { path }, + cfg, + update_test, + }) } fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> { @@ -495,6 +539,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg(), + update_test: UpdateTest::default(), }; Some(res) } @@ -515,7 +560,7 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"]; - docs_from_attrs(attrs).map_or(false, |doc| { + docs_from_attrs(attrs).is_some_and(|doc| { let mut in_code_block = false; for line in doc.lines() { @@ -575,6 +620,128 @@ fn has_test_function_or_multiple_test_submodules( number_of_test_submodules > 1 } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UpdateTest { + pub expect_test: bool, + pub insta: bool, + pub snapbox: bool, +} + +static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<ModPath>>> = OnceLock::new(); + +impl UpdateTest { + const EXPECT_CRATE: &str = "expect_test"; + const EXPECT_MACROS: &[&str] = &["expect", "expect_file"]; + + const INSTA_CRATE: &str = "insta"; + const INSTA_MACROS: &[&str] = &[ + "assert_snapshot", + "assert_debug_snapshot", + "assert_display_snapshot", + "assert_json_snapshot", + "assert_yaml_snapshot", + "assert_ron_snapshot", + "assert_toml_snapshot", + "assert_csv_snapshot", + "assert_compact_json_snapshot", + "assert_compact_debug_snapshot", + "assert_binary_snapshot", + ]; + + const SNAPBOX_CRATE: &str = "snapbox"; + const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"]; + + fn find_snapshot_macro( + sema: &Semantics<'_, RootDatabase>, + scope: &SyntaxNode, + file_range: hir::FileRange, + ) -> Self { + fn init<'a>( + krate_name: &'a str, + paths: &[&str], + map: &mut FxHashMap<&'a str, Vec<ModPath>>, + ) { + let mut res = Vec::with_capacity(paths.len()); + let krate = Name::new_symbol_root(Symbol::intern(krate_name)); + for path in paths { + let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))]; + let mod_path = ModPath::from_segments(PathKind::Abs, segments); + res.push(mod_path); + } + map.insert(krate_name, res); + } + + let mod_paths = SNAPSHOT_TEST_MACROS.get_or_init(|| { + let mut map = FxHashMap::default(); + init(Self::EXPECT_CRATE, Self::EXPECT_MACROS, &mut map); + init(Self::INSTA_CRATE, Self::INSTA_MACROS, &mut map); + init(Self::SNAPBOX_CRATE, Self::SNAPBOX_MACROS, &mut map); + map + }); + + let search_scope = SearchScope::file_range(file_range); + let find_macro = |paths: &[ModPath]| { + for path in paths { + let Some(items) = sema.resolve_mod_path(scope, path) else { + continue; + }; + for item in items { + if let hir::ItemInNs::Macros(makro) = item { + if Definition::Macro(makro) + .usages(sema) + .in_scope(&search_scope) + .at_least_one() + { + return true; + } + } + } + } + false + }; + + UpdateTest { + expect_test: find_macro(mod_paths.get(Self::EXPECT_CRATE).unwrap()), + insta: find_macro(mod_paths.get(Self::INSTA_CRATE).unwrap()), + snapbox: find_macro(mod_paths.get(Self::SNAPBOX_CRATE).unwrap()), + } + } + + pub fn label(&self) -> Option<SmolStr> { + let mut builder: SmallVec<[_; 3]> = SmallVec::new(); + if self.expect_test { + builder.push("Expect"); + } + if self.insta { + builder.push("Insta"); + } + if self.snapbox { + builder.push("Snapbox"); + } + + let res: SmolStr = builder.join(" + ").into(); + if res.is_empty() { + None + } else { + Some(format_smolstr!("↺\u{fe0e} Update Tests ({res})")) + } + } + + pub fn env(&self) -> ArrayVec<(&str, &str), 3> { + let mut env = ArrayVec::new(); + if self.expect_test { + env.push(("UPDATE_EXPECT", "1")); + } + if self.insta { + env.push(("INSTA_UPDATE", "always")); + } + if self.snapbox { + env.push(("SNAPSHOTS", "overwrite")); + } + env + } +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -1337,18 +1504,18 @@ mod tests { file_id: FileId( 0, ), - full_range: 52..115, - focus_range: 67..75, - name: "foo_test", + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", kind: Function, }, NavigationTarget { file_id: FileId( 0, ), - full_range: 121..185, - focus_range: 136..145, - name: "foo2_test", + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", kind: Function, }, ] diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 516f64959ce..84ccadc8c4e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -165,7 +165,7 @@ fn signature_help_for_call( if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) { let inside_callable = callable .arg_list() - .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())); + .is_some_and(|it| it.syntax().text_range().contains(token.text_range().start())); if inside_callable { break callable; } @@ -650,7 +650,7 @@ fn signature_help_for_tuple_pat_ish( ) -> SignatureHelp { let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_))); let is_left_of_rest_pat = - rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); + rest_pat.is_none_or(|it| token.text_range().start() < it.syntax().text_range().end()); let commas = pat .children_with_tokens() diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 0f4b5e7d87a..700e166b238 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -13,11 +13,10 @@ use ide_db::{ use span::Edition; use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T}; -use crate::inlay_hints::InlayFieldsToResolve; use crate::navigation_target::UpmappingResult; use crate::{ - hover::hover_for_definition, - inlay_hints::AdjustmentHintsMode, + hover::{hover_for_definition, SubstTyLen}, + inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve}, moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind}, parent_module::crates_for, Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, @@ -49,7 +48,6 @@ pub struct TokenStaticData { pub references: Vec<ReferenceData>, pub moniker: Option<MonikerResult>, pub display_name: Option<String>, - pub enclosing_moniker: Option<MonikerResult>, pub signature: Option<String>, pub kind: SymbolInformationKind, } @@ -186,6 +184,7 @@ impl StaticIndex<'_> { max_trait_assoc_items_count: None, max_fields_count: Some(5), max_enum_variants_count: Some(5), + max_subst_ty_len: SubstTyLen::Unlimited, }; let tokens = tokens.filter(|token| { matches!( @@ -210,6 +209,7 @@ impl StaticIndex<'_> { &sema, file_id, def, + None, &node, None, false, @@ -224,9 +224,6 @@ impl StaticIndex<'_> { display_name: def .name(self.db) .map(|name| name.display(self.db, edition).to_string()), - enclosing_moniker: current_crate - .zip(def.enclosing_definition(self.db)) - .and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)), signature: Some(def.label(self.db, edition)), kind: def_to_kind(self.db, def), }); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 0747d1b404b..f53f0aec098 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -346,7 +346,7 @@ fn traverse( macro_highlighter = MacroHighlighter::default(); } Some(item) - if attr_or_derive_item.as_ref().map_or(false, |it| *it.item() == item) => + if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) => { attr_or_derive_item = None; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 3767a3917ce..4f3d5d9d00c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -76,7 +76,7 @@ pub(super) fn name_like( Some(IdentClass::NameClass(NameClass::Definition(def))) => { highlight_def(sema, krate, def) | HlMod::Definition } - Some(IdentClass::NameRefClass(NameRefClass::Definition(def))) => { + Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => { highlight_def(sema, krate, def) } // FIXME: Fallback for 'static and '_, as we do not resolve these yet @@ -155,7 +155,7 @@ fn punctuation( if parent .as_ref() .and_then(SyntaxNode::parent) - .map_or(false, |it| it.kind() == MACRO_RULES) => + .is_some_and(|it| it.kind() == MACRO_RULES) => { return HlOperator::Other.into() } @@ -193,7 +193,7 @@ fn keyword( T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, T![const] - if token.parent().map_or(false, |it| { + if token.parent().is_some_and(|it| { matches!( it.kind(), SyntaxKind::CONST @@ -253,14 +253,14 @@ fn highlight_name_ref( && !sema .hir_file_for(name_ref.syntax()) .macro_file() - .map_or(false, |it| it.is_derive_attr_pseudo_expansion(sema.db)) => + .is_some_and(|it| it.is_derive_attr_pseudo_expansion(sema.db)) => { return HlTag::Symbol(SymbolKind::Attribute).into(); } None => return HlTag::UnresolvedReference.into(), }; let mut h = match name_class { - NameRefClass::Definition(def) => { + NameRefClass::Definition(def, _) => { if let Definition::Local(local) = &def { let name = local.name(db); let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); @@ -275,7 +275,7 @@ fn highlight_name_ref( } Definition::Trait(trait_) if trait_.is_unsafe(db) => { if ast::Impl::for_trait_name_ref(&name_ref) - .map_or(false, |impl_| impl_.unsafe_token().is_some()) + .is_some_and(|impl_| impl_.unsafe_token().is_some()) { h |= HlMod::Unsafe; } @@ -550,7 +550,7 @@ pub(super) fn highlight_def( let def_crate = def.krate(db); let is_from_other_crate = def_crate != Some(krate); - let is_from_builtin_crate = def_crate.map_or(false, |def_crate| def_crate.is_builtin(db)); + let is_from_builtin_crate = def_crate.is_some_and(|def_crate| def_crate.is_builtin(db)); let is_builtin = matches!( def, Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) @@ -688,7 +688,7 @@ fn highlight_name_ref_by_syntax( let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) .and_then(|field_expr| sema.resolve_field(&field_expr)) - .map_or(false, |field| match field { + .is_some_and(|field| match field { Either::Left(field) => { matches!(field.parent_def(sema.db), hir::VariantDef::Union(_)) } @@ -764,5 +764,5 @@ fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[Sy } fn parent_matches<N: AstNode>(token: &SyntaxToken) -> bool { - token.parent().map_or(false, |it| N::can_cast(it.kind())) + token.parent().is_some_and(|it| N::can_cast(it.kind())) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 5583f1bc8df..0a157c157c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -28,7 +28,7 @@ pub(super) fn ra_fixture( expanded: &ast::String, ) -> Option<()> { let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?; - if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { + if !active_parameter.ident().is_some_and(|name| name.text().starts_with("ra_fixture")) { return None; } let value = literal.value().ok()?; @@ -279,9 +279,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri .descendants_with_tokens() .filter_map(NodeOrToken::into_token) .filter_map(ast::String::cast) - .find(|string| { - string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) - }) + .find(|string| string.text().get(1..string.text().len() - 1) == Some(text)) } _ => None, } diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index d37318ff457..8998934e0e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -174,7 +174,7 @@ fn on_delimited_node_typed( kinds: &[fn(SyntaxKind) -> bool], ) -> Option<TextEdit> { let t = reparsed.syntax().token_at_offset(offset).right_biased()?; - if t.prev_token().map_or(false, |t| t.kind().is_any_identifier()) { + if t.prev_token().is_some_and(|t| t.kind().is_any_identifier()) { return None; } let (filter, node) = t diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index c15751e7c68..66b8900109c 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -361,6 +361,7 @@ define_symbols! { partial_ord, PartialEq, PartialOrd, + CoercePointee, path, Pending, phantom_data, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 1b2162dad0f..73899408652 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -14,7 +14,7 @@ use ide_db::{ prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase, }; use itertools::Itertools; -use proc_macro_api::{MacroDylib, ProcMacroServer}; +use proc_macro_api::{MacroDylib, ProcMacroClient}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; use vfs::{ @@ -42,7 +42,7 @@ pub fn load_workspace_at( cargo_config: &CargoConfig, load_config: &LoadCargoConfig, progress: &dyn Fn(String), -) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroServer>)> { +) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> { let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); let root = ProjectManifest::discover_single(&root)?; let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; @@ -59,7 +59,7 @@ pub fn load_workspace( ws: ProjectWorkspace, extra_env: &FxHashMap<String, String>, load_config: &LoadCargoConfig, -) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroServer>)> { +) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> { let (sender, receiver) = unbounded(); let mut vfs = vfs::Vfs::default(); let mut loader = { @@ -71,10 +71,10 @@ pub fn load_workspace( let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into)) + .and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into)) .map_err(|e| (e, true)), ProcMacroServerChoice::Explicit(path) => { - ProcMacroServer::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true)) + ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true)) } ProcMacroServerChoice::None => { Err((anyhow::format_err!("proc macro server disabled"), false)) @@ -82,7 +82,7 @@ pub fn load_workspace( }; match &proc_macro_server { Ok(server) => { - tracing::info!(path=%server.path(), "Proc-macro server started") + tracing::info!(path=%server.server_path(), "Proc-macro server started") } Err((e, _)) => { tracing::info!(%e, "Failed to start proc-macro server") @@ -362,7 +362,7 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, disabling all expanders whose names are in `ignored_macros`. pub fn load_proc_macro( - server: &ProcMacroServer, + server: &ProcMacroClient, path: &AbsPath, ignored_macros: &[Box<str>], ) -> ProcMacroLoadResult { @@ -476,17 +476,17 @@ struct Expander(proc_macro_api::ProcMacro); impl ProcMacroExpander for Expander { fn expand( &self, - subtree: &tt::Subtree<Span>, - attrs: Option<&tt::Subtree<Span>>, + subtree: &tt::TopSubtree<Span>, + attrs: Option<&tt::TopSubtree<Span>>, env: &Env, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option<String>, - ) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> { + ) -> Result<tt::TopSubtree<Span>, ProcMacroExpansionError> { match self.0.expand( - subtree, - attrs, + subtree.view(), + attrs.map(|attrs| attrs.view()), env.clone().into(), def_site, call_site, diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 270bc05a4ee..89c30030037 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -53,11 +53,11 @@ fn benchmark_expand_macro_rules() { .map(|(id, tt)| { let res = rules[&id].expand(&tt, |_| (), DUMMY, Edition::CURRENT); assert!(res.err.is_none()); - res.value.0.token_trees.len() + res.value.0 .0.len() }) .sum() }; - assert_eq!(hash, 65720); + assert_eq!(hash, 450144); } fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> { @@ -68,7 +68,7 @@ fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> { .collect() } -fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> { +fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::TopSubtree<Span>> { let fixture = bench_fixture::numerous_macro_rules(); let source_file = ast::SourceFile::parse(&fixture, span::Edition::CURRENT).ok().unwrap(); @@ -92,7 +92,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> { /// Generate random invocation fixtures from rules fn invocation_fixtures( rules: &FxHashMap<String, DeclarativeMacro>, -) -> Vec<(String, tt::Subtree<Span>)> { +) -> Vec<(String, tt::TopSubtree<Span>)> { let mut seed = 123456789; let mut res = Vec::new(); @@ -112,19 +112,16 @@ fn invocation_fixtures( // So we just skip any error cases and try again let mut try_cnt = 0; loop { - let mut token_trees = Vec::new(); + let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter { + open: DUMMY, + close: DUMMY, + kind: tt::DelimiterKind::Invisible, + }); for op in rule.lhs.iter() { - collect_from_op(op, &mut token_trees, &mut seed); + collect_from_op(op, &mut builder, &mut seed); } + let subtree = builder.build(); - let subtree = tt::Subtree { - delimiter: tt::Delimiter { - open: DUMMY, - close: DUMMY, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: token_trees.into_boxed_slice(), - }; if it.expand(&subtree, |_| (), DUMMY, Edition::CURRENT).err.is_none() { res.push((name.clone(), subtree)); break; @@ -139,43 +136,41 @@ fn invocation_fixtures( } return res; - fn collect_from_op(op: &Op, token_trees: &mut Vec<tt::TokenTree<Span>>, seed: &mut usize) { + fn collect_from_op(op: &Op, builder: &mut tt::TopSubtreeBuilder<Span>, seed: &mut usize) { return match op { Op::Var { kind, .. } => match kind.as_ref() { - Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Ty) => token_trees.push(make_ident("Foo")), - Some(MetaVarKind::Tt) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Vis) => token_trees.push(make_ident("pub")), - Some(MetaVarKind::Pat) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Path) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Literal) => token_trees.push(make_literal("1")), - Some(MetaVarKind::Expr(_)) => token_trees.push(make_ident("foo")), + Some(MetaVarKind::Ident) => builder.push(make_ident("foo")), + Some(MetaVarKind::Ty) => builder.push(make_ident("Foo")), + Some(MetaVarKind::Tt) => builder.push(make_ident("foo")), + Some(MetaVarKind::Vis) => builder.push(make_ident("pub")), + Some(MetaVarKind::Pat) => builder.push(make_ident("foo")), + Some(MetaVarKind::Path) => builder.push(make_ident("foo")), + Some(MetaVarKind::Literal) => builder.push(make_literal("1")), + Some(MetaVarKind::Expr(_)) => builder.push(make_ident("foo")), Some(MetaVarKind::Lifetime) => { - token_trees.push(make_punct('\'')); - token_trees.push(make_ident("a")); - } - Some(MetaVarKind::Block) => { - token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)) + builder.push(make_punct('\'')); + builder.push(make_ident("a")); } + Some(MetaVarKind::Block) => make_subtree(tt::DelimiterKind::Brace, builder), Some(MetaVarKind::Item) => { - token_trees.push(make_ident("fn")); - token_trees.push(make_ident("foo")); - token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); - token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)); + builder.push(make_ident("fn")); + builder.push(make_ident("foo")); + make_subtree(tt::DelimiterKind::Parenthesis, builder); + make_subtree(tt::DelimiterKind::Brace, builder); } Some(MetaVarKind::Meta) => { - token_trees.push(make_ident("foo")); - token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); + builder.push(make_ident("foo")); + make_subtree(tt::DelimiterKind::Parenthesis, builder); } None => (), Some(kind) => panic!("Unhandled kind {kind:?}"), }, - Op::Literal(it) => token_trees.push(tt::Leaf::from(it.clone()).into()), - Op::Ident(it) => token_trees.push(tt::Leaf::from(it.clone()).into()), + Op::Literal(it) => builder.push(tt::Leaf::from(it.clone())), + Op::Ident(it) => builder.push(tt::Leaf::from(it.clone())), Op::Punct(puncts) => { for punct in puncts.as_slice() { - token_trees.push(tt::Leaf::from(*punct).into()); + builder.push(tt::Leaf::from(*punct)); } } Op::Repeat { tokens, kind, separator } => { @@ -187,20 +182,18 @@ fn invocation_fixtures( }; for i in 0..cnt { for it in tokens.iter() { - collect_from_op(it, token_trees, seed); + collect_from_op(it, builder, seed); } if i + 1 != cnt { if let Some(sep) = separator { match &**sep { Separator::Literal(it) => { - token_trees.push(tt::Leaf::Literal(it.clone()).into()) - } - Separator::Ident(it) => { - token_trees.push(tt::Leaf::Ident(it.clone()).into()) + builder.push(tt::Leaf::Literal(it.clone())) } + Separator::Ident(it) => builder.push(tt::Leaf::Ident(it.clone())), Separator::Puncts(puncts) => { for it in puncts { - token_trees.push(tt::Leaf::Punct(*it).into()) + builder.push(tt::Leaf::Punct(*it)) } } }; @@ -209,15 +202,9 @@ fn invocation_fixtures( } } Op::Subtree { tokens, delimiter } => { - let mut subtree = Vec::new(); - tokens.iter().for_each(|it| { - collect_from_op(it, &mut subtree, seed); - }); - - let subtree = - tt::Subtree { delimiter: *delimiter, token_trees: subtree.into_boxed_slice() }; - - token_trees.push(subtree.into()); + builder.open(delimiter.kind, delimiter.open); + tokens.iter().for_each(|it| collect_from_op(it, builder, seed)); + builder.close(delimiter.close); } Op::Ignore { .. } | Op::Index { .. } @@ -233,35 +220,27 @@ fn invocation_fixtures( *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); *seed } - fn make_ident(ident: &str) -> tt::TokenTree<Span> { + fn make_ident(ident: &str) -> tt::Leaf<Span> { tt::Leaf::Ident(tt::Ident { span: DUMMY, sym: Symbol::intern(ident), is_raw: tt::IdentIsRaw::No, }) - .into() } - fn make_punct(char: char) -> tt::TokenTree<Span> { - tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into() + fn make_punct(char: char) -> tt::Leaf<Span> { + tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }) } - fn make_literal(lit: &str) -> tt::TokenTree<Span> { + fn make_literal(lit: &str) -> tt::Leaf<Span> { tt::Leaf::Literal(tt::Literal { span: DUMMY, symbol: Symbol::intern(lit), kind: tt::LitKind::Str, suffix: None, }) - .into() } - fn make_subtree( - kind: tt::DelimiterKind, - token_trees: Option<Vec<tt::TokenTree<Span>>>, - ) -> tt::TokenTree<Span> { - tt::Subtree { - delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind }, - token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(), - } - .into() + fn make_subtree(kind: tt::DelimiterKind, builder: &mut tt::TopSubtreeBuilder<Span>) { + builder.open(kind, DUMMY); + builder.close(DUMMY); } } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 1979e5171ab..5539a88c707 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -13,12 +13,12 @@ use crate::{parser::MetaVarKind, ExpandError, ExpandErrorKind, ExpandResult, Mat pub(crate) fn expand_rules( rules: &[crate::Rule], - input: &tt::Subtree<Span>, + input: &tt::TopSubtree<Span>, marker: impl Fn(&mut Span) + Copy, call_site: Span, def_site_edition: Edition, -) -> ExpandResult<(tt::Subtree<Span>, MatchedArmIndex)> { - let mut match_: Option<(matcher::Match, &crate::Rule, usize)> = None; +) -> ExpandResult<(tt::TopSubtree<Span>, MatchedArmIndex)> { + let mut match_: Option<(matcher::Match<'_>, &crate::Rule, usize)> = None; for (idx, rule) in rules.iter().enumerate() { let new_match = matcher::match_(&rule.lhs, input, def_site_edition); @@ -50,13 +50,7 @@ pub(crate) fn expand_rules( ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) } } else { ExpandResult::new( - ( - tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(call_site), - token_trees: Box::default(), - }, - None, - ), + (tt::TopSubtree::empty(tt::DelimSpan::from_single(call_site)), None), ExpandError::new(call_site, ExpandErrorKind::NoMatchingRule), ) } @@ -107,32 +101,35 @@ pub(crate) fn expand_rules( /// In other words, `Bindings` is a *multi* mapping from `Symbol` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -struct Bindings { - inner: FxHashMap<Symbol, Binding>, +#[derive(Debug, Default, Clone)] +struct Bindings<'a> { + inner: FxHashMap<Symbol, Binding<'a>>, } -#[derive(Debug, Clone, PartialEq, Eq)] -enum Binding { - Fragment(Fragment), - Nested(Vec<Binding>), +#[derive(Debug, Clone)] +enum Binding<'a> { + Fragment(Fragment<'a>), + Nested(Vec<Binding<'a>>), Empty, Missing(MetaVarKind), } -#[derive(Debug, Clone, PartialEq, Eq)] -enum Fragment { +#[derive(Debug, Default, Clone)] +enum Fragment<'a> { + #[default] Empty, /// token fragments are just copy-pasted into the output - Tokens(tt::TokenTree<Span>), - /// Expr ast fragments are surrounded with `()` on insertion to preserve - /// precedence. Note that this impl is different from the one currently in - /// `rustc` -- `rustc` doesn't translate fragments into token trees at all. + Tokens(tt::TokenTreesView<'a, Span>), + /// Expr ast fragments are surrounded with `()` on transcription to preserve precedence. + /// Note that this impl is different from the one currently in `rustc` -- + /// `rustc` doesn't translate fragments into token trees at all. /// /// At one point in time, we tried to use "fake" delimiters here à la /// proc-macro delimiter=none. As we later discovered, "none" delimiters are /// tricky to handle in the parser, and rustc doesn't handle those either. - Expr(tt::Subtree<Span>), + /// + /// The span of the outer delimiters is marked on transcription. + Expr(tt::TokenTreesView<'a, Span>), /// There are roughly two types of paths: paths in expression context, where a /// separator `::` between an identifier and its following generic argument list /// is mandatory, and paths in type context, where `::` can be omitted. @@ -142,5 +139,18 @@ enum Fragment { /// and is trasncribed as an expression-context path, verbatim transcription /// would cause a syntax error. We need to fix it up just before transcribing; /// see `transcriber::fix_up_and_push_path_tt()`. - Path(tt::Subtree<Span>), + Path(tt::TokenTreesView<'a, Span>), + TokensOwned(tt::TopSubtree<Span>), +} + +impl Fragment<'_> { + fn is_empty(&self) -> bool { + match self { + Fragment::Empty => true, + Fragment::Tokens(it) => it.len() == 0, + Fragment::Expr(it) => it.len() == 0, + Fragment::Path(it) => it.len() == 0, + Fragment::TokensOwned(it) => it.0.is_empty(), + } + } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 95641abc0f3..b7f25aa3809 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -64,7 +64,10 @@ use std::{rc::Rc, sync::Arc}; use intern::{sym, Symbol}; use smallvec::{smallvec, SmallVec}; use span::{Edition, Span}; -use tt::{iter::TtIter, DelimSpan}; +use tt::{ + iter::{TtElement, TtIter}, + DelimSpan, +}; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -73,7 +76,7 @@ use crate::{ ExpandError, ExpandErrorKind, MetaTemplate, ValueResult, }; -impl Bindings { +impl<'a> Bindings<'a> { fn push_optional(&mut self, name: Symbol) { self.inner.insert(name, Binding::Fragment(Fragment::Empty)); } @@ -82,14 +85,14 @@ impl Bindings { self.inner.insert(name, Binding::Empty); } - fn bindings(&self) -> impl Iterator<Item = &Binding> { + fn bindings(&self) -> impl Iterator<Item = &Binding<'a>> { self.inner.values() } } -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(super) struct Match { - pub(super) bindings: Bindings, +#[derive(Clone, Default, Debug)] +pub(super) struct Match<'a> { + pub(super) bindings: Bindings<'a>, /// We currently just keep the first error and count the rest to compare matches. pub(super) err: Option<ExpandError>, pub(super) err_count: usize, @@ -99,7 +102,7 @@ pub(super) struct Match { pub(super) bound_count: usize, } -impl Match { +impl Match<'_> { fn add_err(&mut self, err: ExpandError) { let prev_err = self.err.take(); self.err = prev_err.or(Some(err)); @@ -108,12 +111,16 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>, edition: Edition) -> Match { +pub(super) fn match_<'t>( + pattern: &'t MetaTemplate, + input: &'t tt::TopSubtree<Span>, + edition: Edition, +) -> Match<'t> { let mut res = match_loop(pattern, input, edition); res.bound_count = count(res.bindings.bindings()); return res; - fn count<'a>(bindings: impl Iterator<Item = &'a Binding>) -> usize { + fn count<'a>(bindings: impl Iterator<Item = &'a Binding<'a>>) -> usize { bindings .map(|it| match it { Binding::Fragment(_) => 1, @@ -126,10 +133,10 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>, edition: } #[derive(Debug, Clone)] -enum BindingKind { +enum BindingKind<'a> { Empty(Symbol), Optional(Symbol), - Fragment(Symbol, Fragment), + Fragment(Symbol, Fragment<'a>), Missing(Symbol, MetaVarKind), Nested(usize, usize), } @@ -144,12 +151,12 @@ enum LinkNode<T> { } #[derive(Default)] -struct BindingsBuilder { - nodes: Vec<Vec<LinkNode<Rc<BindingKind>>>>, +struct BindingsBuilder<'a> { + nodes: Vec<Vec<LinkNode<Rc<BindingKind<'a>>>>>, nested: Vec<Vec<LinkNode<usize>>>, } -impl BindingsBuilder { +impl<'a> BindingsBuilder<'a> { fn alloc(&mut self) -> BindingsIdx { let idx = self.nodes.len(); self.nodes.push(Vec::new()); @@ -186,7 +193,7 @@ impl BindingsBuilder { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone())))); } - fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment<'a>) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } @@ -207,11 +214,11 @@ impl BindingsBuilder { idx.0 = new_idx; } - fn build(self, idx: &BindingsIdx) -> Bindings { + fn build(self, idx: &BindingsIdx) -> Bindings<'a> { self.build_inner(&self.nodes[idx.0]) } - fn build_inner(&self, link_nodes: &[LinkNode<Rc<BindingKind>>]) -> Bindings { + fn build_inner(&self, link_nodes: &[LinkNode<Rc<BindingKind<'a>>>]) -> Bindings<'a> { let mut bindings = Bindings::default(); let mut nodes = Vec::new(); self.collect_nodes(link_nodes, &mut nodes); @@ -257,11 +264,11 @@ impl BindingsBuilder { bindings } - fn collect_nested_ref<'a>( - &'a self, + fn collect_nested_ref<'b>( + &'b self, id: usize, len: usize, - nested_refs: &mut Vec<&'a [LinkNode<Rc<BindingKind>>]>, + nested_refs: &mut Vec<&'b [LinkNode<Rc<BindingKind<'a>>>]>, ) { self.nested[id].iter().take(len).for_each(|it| match it { LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]), @@ -269,7 +276,7 @@ impl BindingsBuilder { }); } - fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec<Bindings>) { + fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec<Bindings<'a>>) { let last = &self.nodes[idx]; let mut nested_refs: Vec<&[_]> = Vec::new(); self.nested[nested_idx].iter().for_each(|it| match *it { @@ -280,17 +287,22 @@ impl BindingsBuilder { nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter))); } - fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { + fn collect_nodes_ref<'b>( + &'b self, + id: usize, + len: usize, + nodes: &mut Vec<&'b BindingKind<'a>>, + ) { self.nodes[id].iter().take(len).for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes), }); } - fn collect_nodes<'a>( - &'a self, - link_nodes: &'a [LinkNode<Rc<BindingKind>>], - nodes: &mut Vec<&'a BindingKind>, + fn collect_nodes<'b>( + &'b self, + link_nodes: &'b [LinkNode<Rc<BindingKind<'a>>>], + nodes: &mut Vec<&'b BindingKind<'a>>, ) { link_nodes.iter().for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), @@ -327,7 +339,7 @@ struct MatchState<'t> { bindings: BindingsIdx, /// Cached result of meta variable parsing - meta_result: Option<(TtIter<'t, Span>, ExpandResult<Option<Fragment>>)>, + meta_result: Option<(TtIter<'t, Span>, ExpandResult<Option<Fragment<'t>>>)>, /// Is error occurred in this state, will `poised` to "parent" is_error: bool, @@ -355,8 +367,8 @@ struct MatchState<'t> { fn match_loop_inner<'t>( src: TtIter<'t, Span>, stack: &[TtIter<'t, Span>], - res: &mut Match, - bindings_builder: &mut BindingsBuilder, + res: &mut Match<'t>, + bindings_builder: &mut BindingsBuilder<'t>, cur_items: &mut SmallVec<[MatchState<'t>; 1]>, bb_items: &mut SmallVec<[MatchState<'t>; 1]>, next_items: &mut Vec<MatchState<'t>>, @@ -463,7 +475,7 @@ fn match_loop_inner<'t>( }) } OpDelimited::Op(Op::Subtree { tokens, delimiter }) => { - if let Ok(subtree) = src.clone().expect_subtree() { + if let Ok((subtree, _)) = src.clone().expect_subtree() { if subtree.delimiter.kind == delimiter.kind { item.stack.push(item.dot); item.dot = tokens.iter_delimited_with(*delimiter); @@ -478,8 +490,8 @@ fn match_loop_inner<'t>( match match_res.err { None => { // Some meta variables are optional (e.g. vis) - if match_res.value.is_some() { - item.meta_result = Some((fork, match_res)); + if !match_res.value.is_empty() { + item.meta_result = Some((fork, match_res.map(Some))); try_push!(bb_items, item); } else { bindings_builder.push_optional(&mut item.bindings, name); @@ -489,15 +501,14 @@ fn match_loop_inner<'t>( } Some(err) => { res.add_err(err); - match match_res.value { - Some(fragment) => bindings_builder.push_fragment( + if !match_res.value.is_empty() { + bindings_builder.push_fragment( &mut item.bindings, name, - fragment, - ), - None => { - bindings_builder.push_missing(&mut item.bindings, name, kind) - } + match_res.value, + ) + } else { + bindings_builder.push_missing(&mut item.bindings, name, kind) } item.is_error = true; error_items.push(item); @@ -593,13 +604,13 @@ fn match_loop_inner<'t>( stdx::never!("metavariable expression in lhs found"); } OpDelimited::Open => { - if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) { + if matches!(src.peek(), Some(TtElement::Subtree(..))) { item.dot.next(); try_push!(next_items, item); } } OpDelimited::Close => { - let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty(); + let is_delim_closed = src.is_empty() && !stack.is_empty(); if is_delim_closed { item.dot.next(); try_push!(next_items, item); @@ -609,9 +620,13 @@ fn match_loop_inner<'t>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) -> Match { - let span = src.delimiter.delim_span(); - let mut src = TtIter::new(src); +fn match_loop<'t>( + pattern: &'t MetaTemplate, + src: &'t tt::TopSubtree<Span>, + edition: Edition, +) -> Match<'t> { + let span = src.top_subtree().delimiter.delim_span(); + let mut src = src.iter(); let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); let mut res = Match::default(); let mut error_recover_item = None; @@ -663,7 +678,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) // We need to do some post processing after the `match_loop_inner`. // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise, // either the parse is ambiguous (which should never happen) or there is a syntax error. - if src.peek_n(0).is_none() && stack.is_empty() { + if src.is_empty() && stack.is_empty() { if let [state] = &*eof_items { // remove all errors, because it is the correct answer ! res = Match::default(); @@ -687,11 +702,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) || !(bb_items.is_empty() || next_items.is_empty()) || bb_items.len() > 1; if has_leftover_tokens { - res.unmatched_tts += src.len(); - while let Some(it) = stack.pop() { - src = it; - res.unmatched_tts += src.len(); - } + res.unmatched_tts += src.remaining().flat_tokens().len(); res.add_err(ExpandError::new(span.open, ExpandErrorKind::LeftoverTokens)); if let Some(error_recover_item) = error_recover_item { @@ -714,9 +725,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) } } else { match src.next() { - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, subtree_iter)) => { stack.push(src.clone()); - src = TtIter::new(subtree); + src = subtree_iter; } None => { if let Some(iter) = stack.pop() { @@ -760,18 +771,16 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) } } -fn match_meta_var( +fn match_meta_var<'t>( kind: MetaVarKind, - input: &mut TtIter<'_, Span>, + input: &mut TtIter<'t, Span>, delim_span: DelimSpan<Span>, edition: Edition, -) -> ExpandResult<Option<Fragment>> { +) -> ExpandResult<Fragment<'t>> { let fragment = match kind { MetaVarKind::Path => { return expect_fragment(input, parser::PrefixEntryPoint::Path, edition, delim_span) - .map(|it| { - it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path) - }); + .map(Fragment::Path); } MetaVarKind::Expr(expr) => { // `expr_2021` should not match underscores, let expressions, or inline const. @@ -782,8 +791,8 @@ fn match_meta_var( // rustc [explicitly checks the next token][1]. // [0]: https://github.com/rust-lang/rust/issues/86730 // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576 - match input.peek_n(0) { - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) => { + match input.peek() { + Some(TtElement::Leaf(tt::Leaf::Ident(it))) => { let is_err = if it.is_raw.no() && matches!(expr, ExprKind::Expr2021) { it.sym == sym::underscore || it.sym == sym::let_ || it.sym == sym::const_ } else { @@ -799,34 +808,15 @@ fn match_meta_var( _ => {} }; return expect_fragment(input, parser::PrefixEntryPoint::Expr, edition, delim_span) - .map(|tt| { - tt.map(|tt| match tt { - tt::TokenTree::Leaf(leaf) => tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(*leaf.span()), - token_trees: Box::new([leaf.into()]), - }, - tt::TokenTree::Subtree(mut s) => { - if s.delimiter.kind == tt::DelimiterKind::Invisible { - s.delimiter.kind = tt::DelimiterKind::Parenthesis; - } - s - } - }) - .map(Fragment::Expr) - }); + .map(Fragment::Expr); } MetaVarKind::Ident | MetaVarKind::Tt | MetaVarKind::Lifetime | MetaVarKind::Literal => { let span = input.next_span(); - let tt_result = match kind { - MetaVarKind::Ident => input - .expect_ident() - .map(|ident| tt::Leaf::from(ident.clone()).into()) - .map_err(|()| { - ExpandError::binding_error( - span.unwrap_or(delim_span.close), - "expected ident", - ) - }), + let savepoint = input.savepoint(); + let err = match kind { + MetaVarKind::Ident => input.expect_ident().map(drop).map_err(|()| { + ExpandError::binding_error(span.unwrap_or(delim_span.close), "expected ident") + }), MetaVarKind::Tt => expect_tt(input).map_err(|()| { ExpandError::binding_error( span.unwrap_or(delim_span.close), @@ -840,29 +830,19 @@ fn match_meta_var( ) }), MetaVarKind::Literal => { - let neg = eat_char(input, '-'); - input - .expect_literal() - .map(|literal| { - let lit = literal.clone(); - match neg { - None => lit.into(), - Some(neg) => tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(*literal.span()), - token_trees: Box::new([neg, lit.into()]), - }), - } - }) - .map_err(|()| { - ExpandError::binding_error( - span.unwrap_or(delim_span.close), - "expected literal", - ) - }) + eat_char(input, '-'); + input.expect_literal().map(drop).map_err(|()| { + ExpandError::binding_error( + span.unwrap_or(delim_span.close), + "expected literal", + ) + }) } _ => unreachable!(), - }; - return tt_result.map(|it| Some(Fragment::Tokens(it))).into(); + } + .err(); + let tt_result = input.from_savepoint(savepoint); + return ValueResult { value: Fragment::Tokens(tt_result), err }; } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, @@ -873,7 +853,7 @@ fn match_meta_var( MetaVarKind::Item => parser::PrefixEntryPoint::Item, MetaVarKind::Vis => parser::PrefixEntryPoint::Vis, }; - expect_fragment(input, fragment, edition, delim_span).map(|it| it.map(Fragment::Tokens)) + expect_fragment(input, fragment, edition, delim_span).map(Fragment::Tokens) } fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) { @@ -990,54 +970,31 @@ fn expect_separator<S: Copy>(iter: &mut TtIter<'_, S>, separator: &Separator) -> ok } -fn expect_tt<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<tt::TokenTree<S>, ()> { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = iter.peek_n(0) { +fn expect_tt<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> { + if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = iter.peek() { if punct.char == '\'' { - expect_lifetime(iter) + expect_lifetime(iter)?; } else { - let puncts = iter.expect_glued_punct()?; - let delimiter = tt::Delimiter { - open: puncts.first().unwrap().span, - close: puncts.last().unwrap().span, - kind: tt::DelimiterKind::Invisible, - }; - let token_trees = puncts.into_iter().map(|p| tt::Leaf::Punct(p).into()).collect(); - Ok(tt::TokenTree::Subtree(tt::Subtree { delimiter, token_trees })) + iter.expect_glued_punct()?; } } else { - iter.next().ok_or(()).cloned() + iter.next().ok_or(())?; } + Ok(()) } -fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<tt::TokenTree<S>, ()> { +fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> { let punct = iter.expect_single_punct()?; if punct.char != '\'' { return Err(()); } - let ident = iter.expect_ident_or_underscore()?; - - Ok(tt::Subtree { - delimiter: tt::Delimiter { - open: punct.span, - close: ident.span, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: Box::new([ - tt::Leaf::Punct(*punct).into(), - tt::Leaf::Ident(ident.clone()).into(), - ]), - } - .into()) + iter.expect_ident_or_underscore()?; + Ok(()) } -fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) -> Option<tt::TokenTree<S>> { - let mut fork = iter.clone(); - match fork.expect_char(c) { - Ok(_) => { - let tt = iter.next().cloned(); - *iter = fork; - tt - } - Err(_) => None, +fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) { + if matches!(iter.peek(), Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char, .. }))) if *char == c) + { + iter.next().expect("already peeked"); } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 1db2f35d262..9255c5a6899 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -3,7 +3,7 @@ use intern::{sym, Symbol}; use span::{Edition, Span}; -use tt::Delimiter; +use tt::{iter::TtElement, Delimiter, TopSubtreeBuilder}; use crate::{ expander::{Binding, Bindings, Fragment}, @@ -11,8 +11,8 @@ use crate::{ ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate, }; -impl Bindings { - fn get(&self, name: &Symbol, span: Span) -> Result<&Binding, ExpandError> { +impl<'t> Bindings<'t> { + fn get(&self, name: &Symbol, span: Span) -> Result<&Binding<'t>, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), None => Err(ExpandError::new( @@ -28,7 +28,7 @@ impl Bindings { mut span: Span, nesting: &mut [NestingState], marker: impl Fn(&mut Span), - ) -> Result<Fragment, ExpandError> { + ) -> Result<Fragment<'t>, ExpandError> { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(span, format!($($arg)*)) }; } @@ -50,86 +50,61 @@ impl Bindings { }; } match b { - Binding::Fragment(f @ (Fragment::Path(sub) | Fragment::Expr(sub))) => { - let tt::Subtree { delimiter, token_trees } = sub; - marker(&mut span); - let subtree = tt::Subtree { - delimiter: tt::Delimiter { - // FIXME split span - open: span, - close: span, - kind: delimiter.kind, - }, - token_trees: token_trees.clone(), - }; - Ok(match f { - Fragment::Tokens(_) | Fragment::Empty => unreachable!(), - Fragment::Expr(_) => Fragment::Expr, - Fragment::Path(_) => Fragment::Path, - }(subtree)) - } - Binding::Fragment(it @ (Fragment::Tokens(_) | Fragment::Empty)) => Ok(it.clone()), + Binding::Fragment(f) => Ok(f.clone()), // emit some reasonable default expansion for missing bindings, // this gives better recovery than emitting the `$fragment-name` verbatim Binding::Missing(it) => Ok({ marker(&mut span); + let mut builder = TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span)); match it { MetaVarKind::Stmt => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { + builder.push(tt::Leaf::Punct(tt::Punct { span, char: ';', spacing: tt::Spacing::Alone, - }))) + })); } - MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: span, - close: span, - kind: tt::DelimiterKind::Brace, - }, - token_trees: Box::new([]), - })), - // FIXME: Meta and Item should get proper defaults - MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => { - Fragment::Empty + MetaVarKind::Block => { + builder.open(tt::DelimiterKind::Brace, span); + builder.close(span); } + // FIXME: Meta and Item should get proper defaults + MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {} MetaVarKind::Path | MetaVarKind::Ty | MetaVarKind::Pat | MetaVarKind::PatParam | MetaVarKind::Expr(_) | MetaVarKind::Ident => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, - }))) + })); } MetaVarKind::Lifetime => { - Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([ - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char: '\'', - span, - spacing: tt::Spacing::Joint, - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: sym::missing.clone(), - span, - is_raw: tt::IdentIsRaw::No, - })), - ]), - })) + builder.extend([ + tt::Leaf::Punct(tt::Punct { + char: '\'', + span, + spacing: tt::Spacing::Joint, + }), + tt::Leaf::Ident(tt::Ident { + sym: sym::missing.clone(), + span, + is_raw: tt::IdentIsRaw::No, + }), + ]); } MetaVarKind::Literal => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, - }))) + })); } } + Fragment::TokensOwned(builder.build()) }), Binding::Nested(_) => { Err(binding_err!("expected simple binding, found nested binding `{name}`")) @@ -143,13 +118,13 @@ impl Bindings { pub(super) fn transcribe( template: &MetaTemplate, - bindings: &Bindings, + bindings: &Bindings<'_>, marker: impl Fn(&mut Span) + Copy, call_site: Span, -) -> ExpandResult<tt::Subtree<Span>> { +) -> ExpandResult<tt::TopSubtree<Span>> { let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), call_site }; - let mut arena: Vec<tt::TokenTree<Span>> = Vec::new(); - expand_subtree(&mut ctx, template, None, &mut arena, marker) + let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(ctx.call_site)); + expand_subtree(&mut ctx, template, &mut builder, marker).map(|()| builder.build()) } #[derive(Debug)] @@ -165,103 +140,97 @@ struct NestingState { #[derive(Debug)] struct ExpandCtx<'a> { - bindings: &'a Bindings, + bindings: &'a Bindings<'a>, nesting: Vec<NestingState>, call_site: Span, } -fn expand_subtree( +fn expand_subtree_with_delimiter( ctx: &mut ExpandCtx<'_>, template: &MetaTemplate, + builder: &mut tt::TopSubtreeBuilder<Span>, delimiter: Option<Delimiter<Span>>, - arena: &mut Vec<tt::TokenTree<Span>>, marker: impl Fn(&mut Span) + Copy, -) -> ExpandResult<tt::Subtree<Span>> { - // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation - let start_elements = arena.len(); +) -> ExpandResult<()> { + let delimiter = delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)); + builder.open(delimiter.kind, delimiter.open); + let result = expand_subtree(ctx, template, builder, marker); + builder.close(delimiter.close); + result +} + +fn expand_subtree( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, + builder: &mut tt::TopSubtreeBuilder<Span>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult<()> { let mut err = None; 'ops: for op in template.iter() { match op { - Op::Literal(it) => arena.push( - tt::Leaf::from({ - let mut it = it.clone(); - marker(&mut it.span); - it - }) - .into(), - ), - Op::Ident(it) => arena.push( - tt::Leaf::from({ - let mut it = it.clone(); - marker(&mut it.span); - it - }) - .into(), - ), + Op::Literal(it) => builder.push(tt::Leaf::from({ + let mut it = it.clone(); + marker(&mut it.span); + it + })), + Op::Ident(it) => builder.push(tt::Leaf::from({ + let mut it = it.clone(); + marker(&mut it.span); + it + })), Op::Punct(puncts) => { - for punct in puncts.as_slice() { - arena.push( - tt::Leaf::from({ - let mut it = *punct; - marker(&mut it.span); - it - }) - .into(), - ); - } + builder.extend(puncts.iter().map(|punct| { + tt::Leaf::from({ + let mut it = *punct; + marker(&mut it.span); + it + }) + })); } Op::Subtree { tokens, delimiter } => { let mut delimiter = *delimiter; marker(&mut delimiter.open); marker(&mut delimiter.close); - let ExpandResult { value: tt, err: e } = - expand_subtree(ctx, tokens, Some(delimiter), arena, marker); + let ExpandResult { value: (), err: e } = + expand_subtree_with_delimiter(ctx, tokens, builder, Some(delimiter), marker); err = err.or(e); - arena.push(tt.into()); } Op::Var { name, id, .. } => { - let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id, marker); + let ExpandResult { value: (), err: e } = + expand_var(ctx, name, *id, builder, marker); err = err.or(e); - push_fragment(ctx, arena, fragment); } Op::Repeat { tokens: subtree, kind, separator } => { - let ExpandResult { value: fragment, err: e } = - expand_repeat(ctx, subtree, *kind, separator.as_deref(), arena, marker); + let ExpandResult { value: (), err: e } = + expand_repeat(ctx, subtree, *kind, separator.as_deref(), builder, marker); err = err.or(e); - push_fragment(ctx, arena, fragment) } Op::Ignore { name, id } => { // Expand the variable, but ignore the result. This registers the repetition count. // FIXME: Any emitted errors are dropped. - expand_var(ctx, name, *id, marker); + let _ = ctx.bindings.get_fragment(name, *id, &mut ctx.nesting, marker); } Op::Index { depth } => { let index = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(index), - span: ctx.call_site, - kind: tt::LitKind::Integer, - suffix: None, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(index), + span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, + })); } Op::Len { depth } => { let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| { // FIXME: to be implemented 0 }); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(length), - span: ctx.call_site, - kind: tt::LitKind::Integer, - suffix: None, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(length), + span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, + })); } Op::Count { name, depth } => { let mut binding = match ctx.bindings.get(name, ctx.call_site) { @@ -302,15 +271,12 @@ fn expand_subtree( let res = count(binding, 0, depth.unwrap_or(0)); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(res), - span: ctx.call_site, - suffix: None, - kind: tt::LitKind::Integer, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(res), + span: ctx.call_site, + suffix: None, + kind: tt::LitKind::Integer, + })); } Op::Concat { elements, span: concat_span } => { let mut concatenated = String::new(); @@ -342,11 +308,22 @@ fn expand_subtree( continue; } }; - let value = match &var_value { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { + let values = match &var_value { + Fragment::Tokens(tokens) => { + let mut iter = tokens.iter(); + (iter.next(), iter.next()) + } + Fragment::TokensOwned(tokens) => { + let mut iter = tokens.iter(); + (iter.next(), iter.next()) + } + _ => (None, None), + }; + let value = match values { + (Some(TtElement::Leaf(tt::Leaf::Ident(ident))), None) => { ident.sym.as_str() } - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + (Some(TtElement::Leaf(tt::Leaf::Literal(lit))), None) => { lit.symbol.as_str() } _ => { @@ -382,36 +359,53 @@ fn expand_subtree( let needs_raw = parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; - arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { is_raw, span: result_span, sym: Symbol::intern(&concatenated), - }))); + })); } } } - // drain the elements added in this instance of expand_subtree - let tts = arena.drain(start_elements..).collect(); - ExpandResult { - value: tt::Subtree { - delimiter: delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)), - token_trees: tts, - }, - err, - } + ExpandResult { value: (), err } } fn expand_var( ctx: &mut ExpandCtx<'_>, v: &Symbol, id: Span, - marker: impl Fn(&mut Span), -) -> ExpandResult<Fragment> { + builder: &mut tt::TopSubtreeBuilder<Span>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult<()> { // We already handle $crate case in mbe parser debug_assert!(*v != sym::crate_); match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) { - Ok(it) => ExpandResult::ok(it), + Ok(fragment) => { + match fragment { + Fragment::Tokens(tt) => builder.extend_with_tt(tt.strip_invisible()), + Fragment::TokensOwned(tt) => builder.extend_with_tt(tt.view().strip_invisible()), + Fragment::Expr(sub) => { + let sub = sub.strip_invisible(); + let mut span = id; + marker(&mut span); + let wrap_in_parens = !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)]) + && sub.try_into_subtree().is_none_or(|it| { + it.top_subtree().delimiter.kind == tt::DelimiterKind::Invisible + }); + if wrap_in_parens { + builder.open(tt::DelimiterKind::Parenthesis, span); + } + builder.extend_with_tt(sub); + if wrap_in_parens { + builder.close(span); + } + } + Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, builder, tt), + Fragment::Empty => (), + }; + ExpandResult::ok(()) + } Err(e) if matches!(e.inner.1, ExpandErrorKind::UnresolvedBinding(_)) => { // Note that it is possible to have a `$var` inside a macro which is not bound. // For example: @@ -426,29 +420,13 @@ fn expand_var( // } // ``` // We just treat it a normal tokens - let tt = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(id), - token_trees: Box::new([ - tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }) - .into(), - tt::Leaf::from(tt::Ident { - sym: v.clone(), - span: id, - is_raw: tt::IdentIsRaw::No, - }) - .into(), - ]), - } - .into(); - ExpandResult::ok(Fragment::Tokens(tt)) + builder.extend([ + tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }), + tt::Leaf::from(tt::Ident { sym: v.clone(), span: id, is_raw: tt::IdentIsRaw::No }), + ]); + ExpandResult::ok(()) } - Err(e) => ExpandResult { - value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty(tt::DelimSpan { - open: ctx.call_site, - close: ctx.call_site, - }))), - err: Some(e), - }, + Err(e) => ExpandResult::only_err(e), } } @@ -457,21 +435,20 @@ fn expand_repeat( template: &MetaTemplate, kind: RepeatKind, separator: Option<&Separator>, - arena: &mut Vec<tt::TokenTree<Span>>, + builder: &mut tt::TopSubtreeBuilder<Span>, marker: impl Fn(&mut Span) + Copy, -) -> ExpandResult<Fragment> { - let mut buf: Vec<tt::TokenTree<Span>> = Vec::new(); +) -> ExpandResult<()> { ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); // Dirty hack to make macro-expansion terminate. // This should be replaced by a proper macro-by-example implementation let limit = 65536; - let mut has_seps = 0; let mut counter = 0; let mut err = None; + let mut restore_point = builder.restore_point(); loop { - let ExpandResult { value: mut t, err: e } = - expand_subtree(ctx, template, None, arena, marker); + let ExpandResult { value: (), err: e } = + expand_subtree_with_delimiter(ctx, template, builder, None, marker); let nesting_state = ctx.nesting.last_mut().unwrap(); if nesting_state.at_end || !nesting_state.hit { break; @@ -479,23 +456,14 @@ fn expand_repeat( nesting_state.idx += 1; nesting_state.hit = false; + builder.remove_last_subtree_if_invisible(); + + restore_point = builder.restore_point(); + counter += 1; if counter == limit { - tracing::warn!( - "expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}", - template, - ctx - ); - return ExpandResult { - value: Fragment::Tokens( - tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), - token_trees: Box::new([]), - } - .into(), - ), - err: Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)), - }; + err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)); + break; } if e.is_some() { @@ -503,24 +471,14 @@ fn expand_repeat( continue; } - t.delimiter.kind = tt::DelimiterKind::Invisible; - push_subtree(&mut buf, t); - if let Some(sep) = separator { - has_seps = match sep { - Separator::Ident(ident) => { - buf.push(tt::Leaf::from(ident.clone()).into()); - 1 - } - Separator::Literal(lit) => { - buf.push(tt::Leaf::from(lit.clone()).into()); - 1 - } + match sep { + Separator::Ident(ident) => builder.push(tt::Leaf::from(ident.clone())), + Separator::Literal(lit) => builder.push(tt::Leaf::from(lit.clone())), Separator::Puncts(puncts) => { for &punct in puncts { - buf.push(tt::Leaf::from(punct).into()); + builder.push(tt::Leaf::from(punct)); } - puncts.len() } }; } @@ -529,46 +487,18 @@ fn expand_repeat( break; } } + // Lose the last separator and last after-the-end round. + builder.restore(restore_point); ctx.nesting.pop().unwrap(); - for _ in 0..has_seps { - buf.pop(); - } // Check if it is a single token subtree without any delimiter // e.g {Delimiter:None> ['>'] /Delimiter:None>} - let tt = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), - token_trees: buf.into_boxed_slice(), - }; - - if RepeatKind::OneOrMore == kind && counter == 0 { - let span = tt.delimiter.open; - return ExpandResult { - value: Fragment::Tokens(tt.into()), - err: Some(ExpandError::new(span, ExpandErrorKind::UnexpectedToken)), - }; - } - ExpandResult { value: Fragment::Tokens(tt.into()), err } -} -fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec<tt::TokenTree<Span>>, fragment: Fragment) { - match fragment { - Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), - Fragment::Expr(sub) => { - push_subtree(buf, sub); - } - Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, buf, tt), - Fragment::Tokens(tt) => buf.push(tt), - Fragment::Empty => (), - } -} - -fn push_subtree(buf: &mut Vec<tt::TokenTree<Span>>, tt: tt::Subtree<Span>) { - match tt.delimiter.kind { - tt::DelimiterKind::Invisible => buf.extend(Vec::from(tt.token_trees)), - _ => buf.push(tt.into()), + if RepeatKind::OneOrMore == kind && counter == 0 && err.is_none() { + err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::UnexpectedToken)); } + ExpandResult { value: (), err } } /// Inserts the path separator `::` between an identifier and its following generic @@ -576,47 +506,45 @@ fn push_subtree(buf: &mut Vec<tt::TokenTree<Span>>, tt: tt::Subtree<Span>) { /// we need this fixup. fn fix_up_and_push_path_tt( ctx: &ExpandCtx<'_>, - buf: &mut Vec<tt::TokenTree<Span>>, - subtree: tt::Subtree<Span>, + builder: &mut tt::TopSubtreeBuilder<Span>, + subtree: tt::TokenTreesView<'_, Span>, ) { - stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; // Note that we only need to fix up the top-level `TokenTree`s because the // context of the paths in the descendant `Subtree`s won't be changed by the // mbe transcription. - for tt in Vec::from(subtree.token_trees) { + let mut iter = subtree.iter(); + while let Some(tt) = iter.next_as_view() { if prev_was_ident { // Pedantically, `(T) -> U` in `FnOnce(T) -> U` is treated as a generic // argument list and thus needs `::` between it and `FnOnce`. However in // today's Rust this type of path *semantically* cannot appear as a // top-level expression-context path, so we can safely ignore it. - if let tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. })) = tt { - buf.push( + if let [tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. }))] = + tt.flat_tokens() + { + builder.extend([ tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Joint, span: ctx.call_site, - }) - .into(), - ); - buf.push( + }), tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Alone, span: ctx.call_site, - }) - .into(), - ); + }), + ]); } } - prev_was_ident = matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(_))); - buf.push(tt); + prev_was_ident = matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(_))]); + builder.extend_with_tt(tt); } } /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth /// defined by the metavar expression. -fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> usize { +fn count(binding: &Binding<'_>, depth_curr: usize, depth_max: usize) -> usize { match binding { Binding::Nested(bs) => { if depth_curr == depth_max { diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index ca10a2be273..6abf56d4b37 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -148,17 +148,17 @@ impl DeclarativeMacro { /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( - tt: &tt::Subtree<Span>, + tt: &tt::TopSubtree<Span>, ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition, ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. - let mut src = TtIter::new(tt); + let mut src = tt.iter(); let mut rules = Vec::new(); let mut err = None; - while src.len() > 0 { + while !src.is_empty() { let rule = match Rule::parse(ctx_edition, &mut src) { Ok(it) => it, Err(e) => { @@ -168,7 +168,7 @@ impl DeclarativeMacro { }; rules.push(rule); if let Err(()) = src.expect_char(';') { - if src.len() > 0 { + if !src.is_empty() { err = Some(Box::new(ParseError::expected("expected `;`"))); } break; @@ -187,8 +187,8 @@ impl DeclarativeMacro { /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( - args: Option<&tt::Subtree<Span>>, - body: &tt::Subtree<Span>, + args: Option<&tt::TopSubtree<Span>>, + body: &tt::TopSubtree<Span>, ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition, ) -> DeclarativeMacro { let mut rules = Vec::new(); @@ -198,8 +198,8 @@ impl DeclarativeMacro { cov_mark::hit!(parse_macro_def_simple); let rule = (|| { - let lhs = MetaTemplate::parse_pattern(ctx_edition, args)?; - let rhs = MetaTemplate::parse_template(ctx_edition, body)?; + let lhs = MetaTemplate::parse_pattern(ctx_edition, args.iter())?; + let rhs = MetaTemplate::parse_template(ctx_edition, body.iter())?; Ok(crate::Rule { lhs, rhs }) })(); @@ -210,8 +210,8 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_rules); - let mut src = TtIter::new(body); - while src.len() > 0 { + let mut src = body.iter(); + while !src.is_empty() { let rule = match Rule::parse(ctx_edition, &mut src) { Ok(it) => it, Err(e) => { @@ -221,7 +221,7 @@ impl DeclarativeMacro { }; rules.push(rule); if let Err(()) = src.expect_any_char(&[';', ',']) { - if src.len() > 0 { + if !src.is_empty() { err = Some(Box::new(ParseError::expected( "expected `;` or `,` to delimit rules", ))); @@ -251,11 +251,11 @@ impl DeclarativeMacro { pub fn expand( &self, - tt: &tt::Subtree<Span>, + tt: &tt::TopSubtree<Span>, marker: impl Fn(&mut Span) + Copy, call_site: Span, def_site_edition: Edition, - ) -> ExpandResult<(tt::Subtree<Span>, MatchedArmIndex)> { + ) -> ExpandResult<(tt::TopSubtree<Span>, MatchedArmIndex)> { expander::expand_rules(&self.rules, tt, marker, call_site, def_site_edition) } } @@ -265,10 +265,12 @@ impl Rule { edition: impl Copy + Fn(SyntaxContextId) -> Edition, src: &mut TtIter<'_, Span>, ) -> Result<Self, ParseError> { - let lhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; + let (_, lhs) = + src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; src.expect_char('=').map_err(|()| ParseError::expected("expected `=`"))?; src.expect_char('>').map_err(|()| ParseError::expected("expected `>`"))?; - let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; + let (_, rhs) = + src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; let lhs = MetaTemplate::parse_pattern(edition, lhs)?; let rhs = MetaTemplate::parse_template(edition, rhs)?; @@ -359,17 +361,17 @@ impl<T: Default, E> From<Result<T, E>> for ValueResult<T, E> { } } -pub fn expect_fragment( - tt_iter: &mut TtIter<'_, Span>, +pub fn expect_fragment<'t>( + tt_iter: &mut TtIter<'t, Span>, entry_point: ::parser::PrefixEntryPoint, edition: ::parser::Edition, delim_span: DelimSpan<Span>, -) -> ExpandResult<Option<tt::TokenTree<Span>>> { +) -> ExpandResult<tt::TokenTreesView<'t, Span>> { use ::parser; - let buffer = tt::buffer::TokenBuffer::from_tokens(tt_iter.as_slice()); - let parser_input = to_parser_input(edition, &buffer); + let buffer = tt_iter.remaining(); + let parser_input = to_parser_input(edition, buffer); let tree_traversal = entry_point.parse(&parser_input, edition); - let mut cursor = buffer.begin(); + let mut cursor = buffer.cursor(); let mut error = false; for step in tree_traversal.iter() { match step { @@ -378,13 +380,13 @@ pub fn expect_fragment( n_input_tokens = 2; } for _ in 0..n_input_tokens { - cursor = cursor.bump_subtree(); + cursor.bump_or_end(); } } parser::Step::FloatSplit { .. } => { // FIXME: We need to split the tree properly here, but mutating the token trees // in the buffer is somewhat tricky to pull off. - cursor = cursor.bump_subtree(); + cursor.bump_or_end(); } parser::Step::Enter { .. } | parser::Step::Exit => (), parser::Step::Error { .. } => error = true, @@ -393,29 +395,19 @@ pub fn expect_fragment( let err = if error || !cursor.is_root() { Some(ExpandError::binding_error( - buffer.begin().token_tree().map_or(delim_span.close, |tt| tt.span()), + buffer.cursor().token_tree().map_or(delim_span.close, |tt| tt.first_span()), format!("expected {entry_point:?}"), )) } else { None }; - let mut curr = buffer.begin(); - let mut res = vec![]; - - while curr != cursor { - let Some(token) = curr.token_tree() else { break }; - res.push(token.cloned()); - curr = curr.bump(); + while !cursor.is_root() { + cursor.bump_or_end(); } - *tt_iter = TtIter::new_iter(tt_iter.as_slice()[res.len()..].iter()); - let res = match &*res { - [] | [_] => res.pop(), - [first, ..] => Some(tt::TokenTree::Subtree(tt::Subtree { - delimiter: Delimiter::invisible_spanned(first.first_span()), - token_trees: res.into_boxed_slice(), - })), - }; + let res = cursor.crossed(); + tt_iter.flat_advance(res.len()); + ExpandResult { value: res, err } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index b55edf4a5e0..16d55492a04 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use arrayvec::ArrayVec; use intern::{sym, Symbol}; use span::{Edition, Span, SyntaxContextId}; -use tt::iter::TtIter; +use tt::iter::{TtElement, TtIter}; use crate::ParseError; @@ -29,14 +29,14 @@ pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); impl MetaTemplate { pub(crate) fn parse_pattern( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - pattern: &tt::Subtree<Span>, + pattern: TtIter<'_, Span>, ) -> Result<Self, ParseError> { MetaTemplate::parse(edition, pattern, Mode::Pattern) } pub(crate) fn parse_template( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - template: &tt::Subtree<Span>, + template: TtIter<'_, Span>, ) -> Result<Self, ParseError> { MetaTemplate::parse(edition, template, Mode::Template) } @@ -47,13 +47,11 @@ impl MetaTemplate { fn parse( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - tt: &tt::Subtree<Span>, + mut src: TtIter<'_, Span>, mode: Mode, ) -> Result<Self, ParseError> { - let mut src = TtIter::new(tt); - let mut res = Vec::new(); - while let Some(first) = src.peek_n(0) { + while let Some(first) = src.peek() { let op = next_op(edition, first, &mut src, mode)?; res.push(op); } @@ -182,12 +180,12 @@ enum Mode { fn next_op( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - first_peeked: &tt::TokenTree<Span>, + first_peeked: TtElement<'_, Span>, src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result<Op, ParseError> { let res = match first_peeked { - tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { + TtElement::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { src.next().expect("first token already peeked"); // Note that the '$' itself is a valid token inside macro_rules. let second = match src.next() { @@ -201,18 +199,16 @@ fn next_op( Some(it) => it, }; match second { - tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { + TtElement::Subtree(subtree, mut subtree_iter) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(edition, subtree, mode)?; + let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?; Op::Repeat { tokens, separator: separator.map(Arc::new), kind } } tt::DelimiterKind::Brace => match mode { - Mode::Template => { - parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| { - ParseError::unexpected("invalid metavariable expression") - })? - } + Mode::Template => parse_metavar_expr(&mut subtree_iter).map_err(|()| { + ParseError::unexpected("invalid metavariable expression") + })?, Mode::Pattern => { return Err(ParseError::unexpected( "`${}` metavariable expressions are not allowed in matchers", @@ -225,7 +221,7 @@ fn next_op( )) } }, - tt::TokenTree::Leaf(leaf) => match leaf { + TtElement::Leaf(leaf) => match leaf { tt::Leaf::Ident(ident) if ident.sym == sym::crate_ => { // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. Op::Ident(tt::Ident { @@ -265,25 +261,25 @@ fn next_op( } } - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => { + TtElement::Leaf(tt::Leaf::Literal(it)) => { src.next().expect("first token already peeked"); Op::Literal(it.clone()) } - tt::TokenTree::Leaf(tt::Leaf::Ident(it)) => { + TtElement::Leaf(tt::Leaf::Ident(it)) => { src.next().expect("first token already peeked"); Op::Ident(it.clone()) } - tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => { + TtElement::Leaf(tt::Leaf::Punct(_)) => { // There's at least one punct so this shouldn't fail. let puncts = src.expect_glued_punct().unwrap(); Op::Punct(Box::new(puncts)) } - tt::TokenTree::Subtree(subtree) => { + TtElement::Subtree(subtree, subtree_iter) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(edition, subtree, mode)?; + let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; @@ -343,8 +339,8 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option<Separator>, Repeat let mut separator = Separator::Puncts(ArrayVec::new()); for tt in src { let tt = match tt { - tt::TokenTree::Leaf(leaf) => leaf, - tt::TokenTree::Subtree(_) => return Err(ParseError::InvalidRepeat), + TtElement::Leaf(leaf) => leaf, + TtElement::Subtree(..) => return Err(ParseError::InvalidRepeat), }; let has_sep = match &separator { Separator::Puncts(puncts) => !puncts.is_empty(), @@ -378,37 +374,39 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option<Separator>, Repeat fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result<Op, ()> { let func = src.expect_ident()?; - let args = src.expect_subtree()?; + let (args, mut args_iter) = src.expect_subtree()?; if args.delimiter.kind != tt::DelimiterKind::Parenthesis { return Err(()); } - let mut args = TtIter::new(args); - let op = match &func.sym { s if sym::ignore == *s => { - args.expect_dollar()?; - let ident = args.expect_ident()?; + args_iter.expect_dollar()?; + let ident = args_iter.expect_ident()?; Op::Ignore { name: ident.sym.clone(), id: ident.span } } - s if sym::index == *s => Op::Index { depth: parse_depth(&mut args)? }, - s if sym::len == *s => Op::Len { depth: parse_depth(&mut args)? }, + s if sym::index == *s => Op::Index { depth: parse_depth(&mut args_iter)? }, + s if sym::len == *s => Op::Len { depth: parse_depth(&mut args_iter)? }, s if sym::count == *s => { - args.expect_dollar()?; - let ident = args.expect_ident()?; - let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; + args_iter.expect_dollar()?; + let ident = args_iter.expect_ident()?; + let depth = if try_eat_comma(&mut args_iter) { + Some(parse_depth(&mut args_iter)?) + } else { + None + }; Op::Count { name: ident.sym.clone(), depth } } s if sym::concat == *s => { let mut elements = Vec::new(); - while let Some(next) = args.peek_n(0) { - let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next { - args.next().expect("already peeked"); + while let Some(next) = args_iter.peek() { + let element = if let TtElement::Leaf(tt::Leaf::Literal(lit)) = next { + args_iter.next().expect("already peeked"); ConcatMetaVarExprElem::Literal(lit.clone()) } else { - let is_var = try_eat_dollar(&mut args); - let ident = args.expect_ident_or_underscore()?.clone(); + let is_var = try_eat_dollar(&mut args_iter); + let ident = args_iter.expect_ident_or_underscore()?.clone(); if is_var { ConcatMetaVarExprElem::Var(ident) @@ -417,8 +415,8 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result<Op, ()> { } }; elements.push(element); - if args.peek_n(0).is_some() { - args.expect_comma()?; + if !args_iter.is_empty() { + args_iter.expect_comma()?; } } if elements.len() < 2 { @@ -429,7 +427,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result<Op, ()> { _ => return Err(()), }; - if args.next().is_some() { + if args_iter.next().is_some() { return Err(()); } @@ -437,7 +435,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result<Op, ()> { } fn parse_depth(src: &mut TtIter<'_, Span>) -> Result<usize, ()> { - if src.len() == 0 { + if src.is_empty() { Ok(0) } else if let tt::Leaf::Literal(tt::Literal { symbol: text, suffix: None, .. }) = src.expect_literal()? @@ -450,7 +448,7 @@ fn parse_depth(src: &mut TtIter<'_, Span>) -> Result<usize, ()> { } fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { + if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek() { let _ = src.next(); return true; } @@ -458,7 +456,7 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { } fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) { + if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek() { let _ = src.next(); return true; } diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs deleted file mode 100644 index 2988fb3cf15..00000000000 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs +++ /dev/null @@ -1,101 +0,0 @@ -use rustc_hash::FxHashMap; -use span::Span; -use syntax::{ast, AstNode}; -use test_utils::extract_annotations; -use tt::{ - buffer::{TokenBuffer, TokenTreeRef}, - Leaf, Punct, Spacing, -}; - -use crate::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; - -fn check_punct_spacing(fixture: &str) { - let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); - let subtree = syntax_node_to_token_tree( - source_file.syntax(), - DummyTestSpanMap, - DUMMY, - DocCommentDesugarMode::Mbe, - ); - let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) - .into_iter() - .map(|(range, annotation)| { - let spacing = match annotation.as_str() { - "Alone" => Spacing::Alone, - "Joint" => Spacing::Joint, - a => panic!("unknown annotation: {a}"), - }; - (range, spacing) - }) - .collect(); - - let buf = TokenBuffer::from_subtree(&subtree); - let mut cursor = buf.begin(); - while !cursor.eof() { - while let Some(token_tree) = cursor.token_tree() { - if let TokenTreeRef::Leaf( - Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }), - _, - ) = token_tree - { - if let Some(expected) = annotations.remove(range) { - assert_eq!(expected, *spacing); - } - } - cursor = cursor.bump_subtree(); - } - cursor = cursor.bump(); - } - - assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}"); -} - -#[test] -fn punct_spacing() { - check_punct_spacing( - r#" -fn main() { - 0+0; - //^ Alone - 0+(0); - //^ Alone - 0<=0; - //^ Joint - // ^ Alone - 0<=(0); - // ^ Alone - a=0; - //^ Alone - a=(0); - //^ Alone - a+=0; - //^ Joint - // ^ Alone - a+=(0); - // ^ Alone - a&&b; - //^ Joint - // ^ Alone - a&&(b); - // ^ Alone - foo::bar; - // ^ Joint - // ^ Alone - use foo::{bar,baz,}; - // ^ Alone - // ^ Alone - // ^ Alone - struct Struct<'a> {}; - // ^ Joint - // ^ Joint - Struct::<0>; - // ^ Alone - Struct::<{0}>; - // ^ Alone - ;; - //^ Joint - // ^ Alone -} - "#, - ); -} diff --git a/src/tools/rust-analyzer/crates/parser/src/input.rs b/src/tools/rust-analyzer/crates/parser/src/input.rs index 9504bd4d9ec..c90b358cfbb 100644 --- a/src/tools/rust-analyzer/crates/parser/src/input.rs +++ b/src/tools/rust-analyzer/crates/parser/src/input.rs @@ -72,7 +72,7 @@ impl Input { } pub(crate) fn is_joint(&self, n: usize) -> bool { let (idx, b_idx) = self.bit_index(n); - self.joint[idx] & 1 << b_idx != 0 + self.joint[idx] & (1 << b_idx) != 0 } } diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index 41d4c68b2d7..386d03a62cc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -85,7 +85,7 @@ impl Output { } pub(crate) fn float_split_hack(&mut self, ends_in_dot: bool) { - let e = (Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT + let e = ((Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT) | ((ends_in_dot as u32) << Self::N_INPUT_TOKEN_SHIFT) | Self::EVENT_MASK; self.event.push(e); @@ -99,7 +99,7 @@ impl Output { } pub(crate) fn leave_node(&mut self) { - let e = (Self::EXIT_EVENT as u32) << Self::TAG_SHIFT | Self::EVENT_MASK; + let e = ((Self::EXIT_EVENT as u32) << Self::TAG_SHIFT) | Self::EVENT_MASK; self.event.push(e) } diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs index 7adedba7c43..32569d5c3fe 100644 --- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs @@ -5,7 +5,7 @@ //! abstract token parsing, and string tokenization as completely separate //! layers. //! -//! However, often you do pares text into syntax trees and the glue code for +//! However, often you do parse text into syntax trees and the glue code for //! that needs to live somewhere. Rather than putting it to lexer or parser, we //! use a separate shortcuts module for that. diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 0c9c6ffd715..318f71a2d4d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -331,6 +331,331 @@ pub enum SyntaxKind { } use self::SyntaxKind::*; impl SyntaxKind { + #[allow(unreachable_patterns)] + pub const fn text(self) -> &'static str { + match self { + TOMBSTONE + | EOF + | __LAST + | BYTE + | BYTE_STRING + | CHAR + | C_STRING + | FLOAT_NUMBER + | INT_NUMBER + | RAW_BYTE_STRING + | RAW_C_STRING + | RAW_STRING + | STRING + | ABI + | ADT + | ARG_LIST + | ARRAY_EXPR + | ARRAY_TYPE + | ASM_CLOBBER_ABI + | ASM_CONST + | ASM_DIR_SPEC + | ASM_EXPR + | ASM_LABEL + | ASM_OPERAND + | ASM_OPERAND_EXPR + | ASM_OPERAND_NAMED + | ASM_OPTION + | ASM_OPTIONS + | ASM_PIECE + | ASM_REG_OPERAND + | ASM_REG_SPEC + | ASM_SYM + | ASSOC_ITEM + | ASSOC_ITEM_LIST + | ASSOC_TYPE_ARG + | ATTR + | AWAIT_EXPR + | BECOME_EXPR + | BIN_EXPR + | BLOCK_EXPR + | BOX_PAT + | BREAK_EXPR + | CALL_EXPR + | CAST_EXPR + | CLOSURE_BINDER + | CLOSURE_EXPR + | CONST + | CONST_ARG + | CONST_BLOCK_PAT + | CONST_PARAM + | CONTINUE_EXPR + | DYN_TRAIT_TYPE + | ENUM + | EXPR + | EXPR_STMT + | EXTERN_BLOCK + | EXTERN_CRATE + | EXTERN_ITEM + | EXTERN_ITEM_LIST + | FIELD_EXPR + | FIELD_LIST + | FN + | FN_PTR_TYPE + | FORMAT_ARGS_ARG + | FORMAT_ARGS_EXPR + | FOR_EXPR + | FOR_TYPE + | GENERIC_ARG + | GENERIC_ARG_LIST + | GENERIC_PARAM + | GENERIC_PARAM_LIST + | IDENT_PAT + | IF_EXPR + | IMPL + | IMPL_TRAIT_TYPE + | INDEX_EXPR + | INFER_TYPE + | ITEM + | ITEM_LIST + | LABEL + | LET_ELSE + | LET_EXPR + | LET_STMT + | LIFETIME + | LIFETIME_ARG + | LIFETIME_PARAM + | LITERAL + | LITERAL_PAT + | LOOP_EXPR + | MACRO_CALL + | MACRO_DEF + | MACRO_EXPR + | MACRO_ITEMS + | MACRO_PAT + | MACRO_RULES + | MACRO_STMTS + | MACRO_TYPE + | MATCH_ARM + | MATCH_ARM_LIST + | MATCH_EXPR + | MATCH_GUARD + | META + | METHOD_CALL_EXPR + | MODULE + | NAME + | NAME_REF + | NEVER_TYPE + | OFFSET_OF_EXPR + | OR_PAT + | PARAM + | PARAM_LIST + | PARENTHESIZED_ARG_LIST + | PAREN_EXPR + | PAREN_PAT + | PAREN_TYPE + | PAT + | PATH + | PATH_EXPR + | PATH_PAT + | PATH_SEGMENT + | PATH_TYPE + | PREFIX_EXPR + | PTR_TYPE + | RANGE_EXPR + | RANGE_PAT + | RECORD_EXPR + | RECORD_EXPR_FIELD + | RECORD_EXPR_FIELD_LIST + | RECORD_FIELD + | RECORD_FIELD_LIST + | RECORD_PAT + | RECORD_PAT_FIELD + | RECORD_PAT_FIELD_LIST + | REF_EXPR + | REF_PAT + | REF_TYPE + | RENAME + | REST_PAT + | RETURN_EXPR + | RETURN_TYPE_SYNTAX + | RET_TYPE + | SELF_PARAM + | SLICE_PAT + | SLICE_TYPE + | SOURCE_FILE + | STATIC + | STMT + | STMT_LIST + | STRUCT + | TOKEN_TREE + | TRAIT + | TRAIT_ALIAS + | TRY_EXPR + | TUPLE_EXPR + | TUPLE_FIELD + | TUPLE_FIELD_LIST + | TUPLE_PAT + | TUPLE_STRUCT_PAT + | TUPLE_TYPE + | TYPE + | TYPE_ALIAS + | TYPE_ARG + | TYPE_BOUND + | TYPE_BOUND_LIST + | TYPE_PARAM + | UNDERSCORE_EXPR + | UNION + | USE + | USE_BOUND_GENERIC_ARG + | USE_BOUND_GENERIC_ARGS + | USE_TREE + | USE_TREE_LIST + | VARIANT + | VARIANT_LIST + | VISIBILITY + | WHERE_CLAUSE + | WHERE_PRED + | WHILE_EXPR + | WILDCARD_PAT + | YEET_EXPR + | YIELD_EXPR + | COMMENT + | ERROR + | IDENT + | LIFETIME_IDENT + | NEWLINE + | SHEBANG + | WHITESPACE => panic!("no text for these `SyntaxKind`s"), + DOLLAR => "$", + SEMICOLON => ";", + COMMA => ",", + L_PAREN => "(", + R_PAREN => ")", + L_CURLY => "{", + R_CURLY => "}", + L_BRACK => "[", + R_BRACK => "]", + L_ANGLE => "<", + R_ANGLE => ">", + AT => "@", + POUND => "#", + TILDE => "~", + QUESTION => "?", + AMP => "&", + PIPE => "|", + PLUS => "+", + STAR => "*", + SLASH => "/", + CARET => "^", + PERCENT => "%", + UNDERSCORE => "_", + DOT => ".", + DOT2 => "..", + DOT3 => "...", + DOT2EQ => "..=", + COLON => ":", + COLON2 => "::", + EQ => "=", + EQ2 => "==", + FAT_ARROW => "=>", + BANG => "!", + NEQ => "!=", + MINUS => "-", + THIN_ARROW => "->", + LTEQ => "<=", + GTEQ => ">=", + PLUSEQ => "+=", + MINUSEQ => "-=", + PIPEEQ => "|=", + AMPEQ => "&=", + CARETEQ => "^=", + SLASHEQ => "/=", + STAREQ => "*=", + PERCENTEQ => "%=", + AMP2 => "&&", + PIPE2 => "||", + SHL => "<<", + SHR => ">>", + SHLEQ => "<<=", + SHREQ => ">>=", + SELF_TYPE_KW => "Self", + ABSTRACT_KW => "abstract", + AS_KW => "as", + BECOME_KW => "become", + BOX_KW => "box", + BREAK_KW => "break", + CONST_KW => "const", + CONTINUE_KW => "continue", + CRATE_KW => "crate", + DO_KW => "do", + ELSE_KW => "else", + ENUM_KW => "enum", + EXTERN_KW => "extern", + FALSE_KW => "false", + FINAL_KW => "final", + FN_KW => "fn", + FOR_KW => "for", + IF_KW => "if", + IMPL_KW => "impl", + IN_KW => "in", + LET_KW => "let", + LOOP_KW => "loop", + MACRO_KW => "macro", + MATCH_KW => "match", + MOD_KW => "mod", + MOVE_KW => "move", + MUT_KW => "mut", + OVERRIDE_KW => "override", + PRIV_KW => "priv", + PUB_KW => "pub", + REF_KW => "ref", + RETURN_KW => "return", + SELF_KW => "self", + STATIC_KW => "static", + STRUCT_KW => "struct", + SUPER_KW => "super", + TRAIT_KW => "trait", + TRUE_KW => "true", + TYPE_KW => "type", + TYPEOF_KW => "typeof", + UNSAFE_KW => "unsafe", + UNSIZED_KW => "unsized", + USE_KW => "use", + VIRTUAL_KW => "virtual", + WHERE_KW => "where", + WHILE_KW => "while", + YIELD_KW => "yield", + ASM_KW => "asm", + ATT_SYNTAX_KW => "att_syntax", + AUTO_KW => "auto", + BUILTIN_KW => "builtin", + CLOBBER_ABI_KW => "clobber_abi", + DEFAULT_KW => "default", + DYN_KW => "dyn", + FORMAT_ARGS_KW => "format_args", + INLATEOUT_KW => "inlateout", + INOUT_KW => "inout", + LABEL_KW => "label", + LATEOUT_KW => "lateout", + MACRO_RULES_KW => "macro_rules", + MAY_UNWIND_KW => "may_unwind", + NOMEM_KW => "nomem", + NORETURN_KW => "noreturn", + NOSTACK_KW => "nostack", + OFFSET_OF_KW => "offset_of", + OPTIONS_KW => "options", + OUT_KW => "out", + PRESERVES_FLAGS_KW => "preserves_flags", + PURE_KW => "pure", + RAW_KW => "raw", + READONLY_KW => "readonly", + SAFE_KW => "safe", + SYM_KW => "sym", + UNION_KW => "union", + YEET_KW => "yeet", + ASYNC_KW => "async", + AWAIT_KW => "await", + DYN_KW => "dyn", + GEN_KW => "gen", + TRY_KW => "try", + } + } #[doc = r" Checks whether this syntax kind is a strict keyword for the given edition."] #[doc = r" Strict keywords are identifiers that are always considered keywords."] pub fn is_strict_keyword(self, edition: Edition) -> bool { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs index ec89f6a9e65..ec89f6a9e65 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index bbd9f582df9..6ea8db9a905 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -9,10 +9,10 @@ use serde_derive::{Deserialize, Serialize}; use crate::ProcMacroKind; -pub use crate::msg::flat::{ +pub use self::flat::{ deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap, - TokenId, }; +pub use span::TokenId; // The versions of the server protocol pub const NO_VERSION_CHECK_VERSION: u32 = 0; @@ -160,11 +160,14 @@ type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) mod tests { use intern::{sym, Symbol}; use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize}; - use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree}; + use tt::{ + Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, + TopSubtreeBuilder, + }; use super::*; - fn fixture_token_tree() -> Subtree<Span> { + fn fixture_token_tree() -> TopSubtree<Span> { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( span::FileId::from_raw(0xe4e4e), @@ -173,93 +176,88 @@ mod tests { ast_id: ErasedFileAstId::from_raw(0), }; - let token_trees = Box::new([ - TokenTree::Leaf( - Ident { - sym: Symbol::intern("struct"), - span: Span { - range: TextRange::at(TextSize::new(0), TextSize::of("struct")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - is_raw: tt::IdentIsRaw::No, - } - .into(), - ), - TokenTree::Leaf( - Ident { - sym: Symbol::intern("Foo"), - span: Span { - range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - is_raw: tt::IdentIsRaw::Yes, - } - .into(), - ), - TokenTree::Leaf(Leaf::Literal(Literal { - symbol: Symbol::intern("Foo"), + let mut builder = TopSubtreeBuilder::new(Delimiter { + open: Span { + range: TextRange::empty(TextSize::new(0)), + anchor, + ctx: SyntaxContextId::ROOT, + }, + close: Span { + range: TextRange::empty(TextSize::new(19)), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: DelimiterKind::Invisible, + }); + + builder.push( + Ident { + sym: Symbol::intern("struct"), span: Span { - range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), + range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, ctx: SyntaxContextId::ROOT, }, - kind: tt::LitKind::Str, - suffix: None, - })), - TokenTree::Leaf(Leaf::Punct(Punct { - char: '@', + is_raw: tt::IdentIsRaw::No, + } + .into(), + ); + builder.push( + Ident { + sym: Symbol::intern("Foo"), span: Span { - range: TextRange::at(TextSize::new(13), TextSize::of('@')), + range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, ctx: SyntaxContextId::ROOT, }, - spacing: Spacing::Joint, - })), - TokenTree::Subtree(Subtree { - delimiter: Delimiter { - open: Span { - range: TextRange::at(TextSize::new(14), TextSize::of('{')), - anchor, - ctx: SyntaxContextId::ROOT, - }, - close: Span { - range: TextRange::at(TextSize::new(19), TextSize::of('}')), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: DelimiterKind::Brace, - }, - token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal { - symbol: sym::INTEGER_0.clone(), - span: Span { - range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: tt::LitKind::Integer, - suffix: Some(sym::u32.clone()), - }))]), - }), - ]); - - Subtree { - delimiter: Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContextId::ROOT, - }, - close: Span { - range: TextRange::empty(TextSize::new(19)), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: DelimiterKind::Invisible, + is_raw: tt::IdentIsRaw::Yes, + } + .into(), + ); + builder.push(Leaf::Literal(Literal { + symbol: Symbol::intern("Foo"), + span: Span { + range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), + anchor, + ctx: SyntaxContextId::ROOT, }, - token_trees, - } + kind: tt::LitKind::Str, + suffix: None, + })); + builder.push(Leaf::Punct(Punct { + char: '@', + span: Span { + range: TextRange::at(TextSize::new(13), TextSize::of('@')), + anchor, + ctx: SyntaxContextId::ROOT, + }, + spacing: Spacing::Joint, + })); + builder.open( + DelimiterKind::Brace, + Span { + range: TextRange::at(TextSize::new(14), TextSize::of('{')), + anchor, + ctx: SyntaxContextId::ROOT, + }, + ); + builder.push(Leaf::Literal(Literal { + symbol: sym::INTEGER_0.clone(), + span: Span { + range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: tt::LitKind::Integer, + suffix: Some(sym::u32.clone()), + })); + builder.close(Span { + range: TextRange::at(TextSize::new(19), TextSize::of('}')), + anchor, + ctx: SyntaxContextId::ROOT, + }); + + builder.build() } #[test] @@ -269,7 +267,7 @@ mod tests { let mut span_data_table = Default::default(); let task = ExpandMacro { data: ExpandMacroData { - macro_body: FlatTree::new(&tt, v, &mut span_data_table), + macro_body: FlatTree::new(tt.view(), v, &mut span_data_table), macro_name: Default::default(), attributes: None, has_global_spans: ExpnGlobals { @@ -289,9 +287,8 @@ mod tests { // println!("{}", json); let back: ExpandMacro = serde_json::from_str(&json).unwrap(); - assert_eq!( - tt, - back.data.macro_body.to_subtree_resolved(v, &span_data_table), + assert!( + tt == back.data.macro_body.to_subtree_resolved(v, &span_data_table), "version: {v}" ); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index ce4b060fca5..c194f301714 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -1,13 +1,13 @@ -//! Serialization-friendly representation of `tt::Subtree`. +//! Serialization-friendly representation of `tt::TopSubtree`. //! -//! It is possible to serialize `Subtree` as is, as a tree, but using +//! It is possible to serialize `TopSubtree` recursively, as a tree, but using //! arbitrary-nested trees in JSON is problematic, as they can cause the JSON //! parser to overflow the stack. //! //! Additionally, such implementation would be pretty verbose, and we do care //! about performance here a bit. //! -//! So what this module does is dumping a `tt::Subtree` into a bunch of flat +//! So what this module does is dumping a `tt::TopSubtree` into a bunch of flat //! array of numbers. See the test in the parent module to get an example //! output. //! @@ -40,9 +40,11 @@ use std::collections::VecDeque; use intern::Symbol; use rustc_hash::FxHashMap; use serde_derive::{Deserialize, Serialize}; -use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange}; +use span::{ + EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TokenId, +}; -use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; +use crate::legacy_protocol::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; pub type SpanDataIndexMap = indexmap::IndexSet<Span, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; @@ -78,15 +80,6 @@ pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { .collect() } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct TokenId(pub u32); - -impl std::fmt::Debug for TokenId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { subtree: Vec<u32>, @@ -125,7 +118,7 @@ struct IdentRepr { impl FlatTree { pub fn new( - subtree: &tt::Subtree<Span>, + subtree: tt::SubtreeView<'_, Span>, version: u32, span_data_table: &mut SpanDataIndexMap, ) -> FlatTree { @@ -166,7 +159,7 @@ impl FlatTree { } } - pub fn new_raw(subtree: &tt::Subtree<TokenId>, version: u32) -> FlatTree { + pub fn new_raw(subtree: tt::SubtreeView<'_, TokenId>, version: u32) -> FlatTree { let mut w = Writer { string_table: FxHashMap::default(), work: VecDeque::new(), @@ -208,7 +201,7 @@ impl FlatTree { self, version: u32, span_data_table: &SpanDataIndexMap, - ) -> tt::Subtree<Span> { + ) -> tt::TopSubtree<Span> { Reader { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) @@ -234,7 +227,7 @@ impl FlatTree { .read() } - pub fn to_subtree_unresolved(self, version: u32) -> tt::Subtree<TokenId> { + pub fn to_subtree_unresolved(self, version: u32) -> tt::TopSubtree<TokenId> { Reader { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) @@ -388,7 +381,7 @@ impl InternableSpan for Span { } struct Writer<'a, 'span, S: InternableSpan> { - work: VecDeque<(usize, &'a tt::Subtree<S>)>, + work: VecDeque<(usize, tt::iter::TtIter<'a, S>)>, string_table: FxHashMap<std::borrow::Cow<'a, str>, u32>, span_data_table: &'span mut S::Table, version: u32, @@ -402,8 +395,9 @@ struct Writer<'a, 'span, S: InternableSpan> { } impl<'a, S: InternableSpan> Writer<'a, '_, S> { - fn write(&mut self, root: &'a tt::Subtree<S>) { - self.enqueue(root); + fn write(&mut self, root: tt::SubtreeView<'a, S>) { + let subtree = root.top_subtree(); + self.enqueue(subtree, root.iter()); while let Some((idx, subtree)) = self.work.pop_front() { self.subtree(idx, subtree); } @@ -413,20 +407,20 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { S::token_id_of(self.span_data_table, span) } - fn subtree(&mut self, idx: usize, subtree: &'a tt::Subtree<S>) { + fn subtree(&mut self, idx: usize, subtree: tt::iter::TtIter<'a, S>) { let mut first_tt = self.token_tree.len(); - let n_tt = subtree.token_trees.len(); + let n_tt = subtree.clone().count(); // FIXME: `count()` walks over the entire iterator. self.token_tree.resize(first_tt + n_tt, !0); self.subtree[idx].tt = [first_tt as u32, (first_tt + n_tt) as u32]; - for child in subtree.token_trees.iter() { + for child in subtree { let idx_tag = match child { - tt::TokenTree::Subtree(it) => { - let idx = self.enqueue(it); + tt::iter::TtElement::Subtree(subtree, subtree_iter) => { + let idx = self.enqueue(subtree, subtree_iter); idx << 2 } - tt::TokenTree::Leaf(leaf) => match leaf { + tt::iter::TtElement::Leaf(leaf) => match leaf { tt::Leaf::Literal(lit) => { let idx = self.literal.len() as u32; let id = self.token_id_of(lit.span); @@ -456,13 +450,13 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { }), suffix, }); - idx << 2 | 0b01 + (idx << 2) | 0b01 } tt::Leaf::Punct(punct) => { let idx = self.punct.len() as u32; let id = self.token_id_of(punct.span); self.punct.push(PunctRepr { char: punct.char, spacing: punct.spacing, id }); - idx << 2 | 0b10 + (idx << 2) | 0b10 } tt::Leaf::Ident(ident) => { let idx = self.ident.len() as u32; @@ -475,7 +469,7 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { self.intern(ident.sym.as_str()) }; self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() }); - idx << 2 | 0b11 + (idx << 2) | 0b11 } }, }; @@ -484,13 +478,13 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { } } - fn enqueue(&mut self, subtree: &'a tt::Subtree<S>) -> u32 { + fn enqueue(&mut self, subtree: &'a tt::Subtree<S>, contents: tt::iter::TtIter<'a, S>) -> u32 { let idx = self.subtree.len(); let open = self.token_id_of(subtree.delimiter.open); let close = self.token_id_of(subtree.delimiter.close); let delimiter_kind = subtree.delimiter.kind; self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] }); - self.work.push_back((idx, subtree)); + self.work.push_back((idx, contents)); idx as u32 } @@ -525,103 +519,110 @@ struct Reader<'span, S: InternableSpan> { } impl<S: InternableSpan> Reader<'_, S> { - pub(crate) fn read(self) -> tt::Subtree<S> { - let mut res: Vec<Option<tt::Subtree<S>>> = vec![None; self.subtree.len()]; + pub(crate) fn read(self) -> tt::TopSubtree<S> { + let mut res: Vec<Option<(tt::Delimiter<S>, Vec<tt::TokenTree<S>>)>> = + vec![None; self.subtree.len()]; let read_span = |id| S::span_for_token_id(self.span_data_table, id); for i in (0..self.subtree.len()).rev() { let repr = &self.subtree[i]; let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize]; - let s = tt::Subtree { - delimiter: tt::Delimiter { - open: read_span(repr.open), - close: read_span(repr.close), - kind: repr.kind, - }, - token_trees: token_trees - .iter() - .copied() - .map(|idx_tag| { - let tag = idx_tag & 0b11; - let idx = (idx_tag >> 2) as usize; - match tag { - // XXX: we iterate subtrees in reverse to guarantee - // that this unwrap doesn't fire. - 0b00 => res[idx].take().unwrap().into(), - 0b01 => { - use tt::LitKind::*; - let repr = &self.literal[idx]; - let text = self.text[repr.text as usize].as_str(); - let span = read_span(repr.id); - tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { - tt::Literal { - symbol: Symbol::intern(text), - span, - kind: match u16::to_le_bytes(repr.kind) { - [0, _] => Err(()), - [1, _] => Byte, - [2, _] => Char, - [3, _] => Integer, - [4, _] => Float, - [5, _] => Str, - [6, r] => StrRaw(r), - [7, _] => ByteStr, - [8, r] => ByteStrRaw(r), - [9, _] => CStr, - [10, r] => CStrRaw(r), - _ => unreachable!(), - }, - suffix: if repr.suffix != !0 { - Some(Symbol::intern( - self.text[repr.suffix as usize].as_str(), - )) - } else { - None - }, - } - } else { - tt::token_to_literal(text, span) - }) - .into() - } - 0b10 => { - let repr = &self.punct[idx]; - tt::Leaf::Punct(tt::Punct { - char: repr.char, - spacing: repr.spacing, - span: read_span(repr.id), - }) - .into() - } - 0b11 => { - let repr = &self.ident[idx]; - let text = self.text[repr.text as usize].as_str(); - let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA { - ( - if repr.is_raw { - tt::IdentIsRaw::Yes - } else { - tt::IdentIsRaw::No - }, - text, - ) - } else { - tt::IdentIsRaw::split_from_symbol(text) - }; - tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern(text), - span: read_span(repr.id), - is_raw, - }) - .into() - } - other => panic!("bad tag: {other}"), - } - }) - .collect(), + let delimiter = tt::Delimiter { + open: read_span(repr.open), + close: read_span(repr.close), + kind: repr.kind, }; - res[i] = Some(s); + let mut s = Vec::new(); + for &idx_tag in token_trees { + let tag = idx_tag & 0b11; + let idx = (idx_tag >> 2) as usize; + match tag { + // XXX: we iterate subtrees in reverse to guarantee + // that this unwrap doesn't fire. + 0b00 => { + let (delimiter, subtree) = res[idx].take().unwrap(); + s.push(tt::TokenTree::Subtree(tt::Subtree { + delimiter, + len: subtree.len() as u32, + })); + s.extend(subtree) + } + 0b01 => { + use tt::LitKind::*; + let repr = &self.literal[idx]; + let text = self.text[repr.text as usize].as_str(); + let span = read_span(repr.id); + s.push( + tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { + tt::Literal { + symbol: Symbol::intern(text), + span, + kind: match u16::to_le_bytes(repr.kind) { + [0, _] => Err(()), + [1, _] => Byte, + [2, _] => Char, + [3, _] => Integer, + [4, _] => Float, + [5, _] => Str, + [6, r] => StrRaw(r), + [7, _] => ByteStr, + [8, r] => ByteStrRaw(r), + [9, _] => CStr, + [10, r] => CStrRaw(r), + _ => unreachable!(), + }, + suffix: if repr.suffix != !0 { + Some(Symbol::intern( + self.text[repr.suffix as usize].as_str(), + )) + } else { + None + }, + } + } else { + tt::token_to_literal(text, span) + }) + .into(), + ) + } + 0b10 => { + let repr = &self.punct[idx]; + s.push( + tt::Leaf::Punct(tt::Punct { + char: repr.char, + spacing: repr.spacing, + span: read_span(repr.id), + }) + .into(), + ) + } + 0b11 => { + let repr = &self.ident[idx]; + let text = self.text[repr.text as usize].as_str(); + let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA { + ( + if repr.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, + text, + ) + } else { + tt::IdentIsRaw::split_from_symbol(text) + }; + s.push( + tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern(text), + span: read_span(repr.id), + is_raw, + }) + .into(), + ) + } + other => panic!("bad tag: {other}"), + } + } + res[i] = Some((delimiter, s)); } - res[0].take().unwrap() + let (delimiter, mut res) = res[0].take().unwrap(); + res.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: res.len() as u32 })); + tt::TopSubtree(res.into_boxed_slice()) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index e54d501b94c..dc3328ebcda 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -5,26 +5,26 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -pub mod json; -pub mod msg; +pub mod legacy_protocol { + pub mod json; + pub mod msg; +} mod process; use paths::{AbsPath, AbsPathBuf}; use span::Span; use std::{fmt, io, sync::Arc}; -use serde::{Deserialize, Serialize}; - use crate::{ - msg::{ + legacy_protocol::msg::{ deserialize_span_data_index_map, flat::serialize_span_data_index_map, ExpandMacro, - ExpnGlobals, FlatTree, PanicMessage, SpanDataIndexMap, HAS_GLOBAL_SPANS, - RUST_ANALYZER_SPAN_SUPPORT, + ExpandMacroData, ExpnGlobals, FlatTree, PanicMessage, Request, Response, SpanDataIndexMap, + HAS_GLOBAL_SPANS, RUST_ANALYZER_SPAN_SUPPORT, }, - process::ProcMacroProcessSrv, + process::ProcMacroServerProcess, }; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub enum ProcMacroKind { CustomDerive, Attr, @@ -37,12 +37,12 @@ pub enum ProcMacroKind { /// A handle to an external process which load dylibs with macros (.so or .dll) /// and runs actual macro expansion functions. #[derive(Debug)] -pub struct ProcMacroServer { +pub struct ProcMacroClient { /// Currently, the proc macro process expands all procedural macros sequentially. /// /// That means that concurrent salsa requests may block each other when expanding proc macros, /// which is unfortunate, but simple and good enough for the time being. - process: Arc<ProcMacroProcessSrv>, + process: Arc<ProcMacroServerProcess>, path: AbsPathBuf, } @@ -56,13 +56,13 @@ impl MacroDylib { } } -/// A handle to a specific macro (a `#[proc_macro]` annotated function). +/// A handle to a specific proc-macro (a `#[proc_macro]` annotated function). /// -/// It exists within a context of a specific [`ProcMacroProcess`] -- currently -/// we share a single expander process for all macros. +/// It exists within the context of a specific proc-macro server -- currently +/// we share a single expander process for all macros within a workspace. #[derive(Debug, Clone)] pub struct ProcMacro { - process: Arc<ProcMacroProcessSrv>, + process: Arc<ProcMacroServerProcess>, dylib_path: Arc<AbsPathBuf>, name: Box<str>, kind: ProcMacroKind, @@ -95,21 +95,22 @@ impl fmt::Display for ServerError { } } -impl ProcMacroServer { +impl ProcMacroClient { /// Spawns an external process as the proc macro server and returns a client connected to it. pub fn spawn( process_path: &AbsPath, env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)> + Clone, - ) -> io::Result<ProcMacroServer> { - let process = ProcMacroProcessSrv::run(process_path, env)?; - Ok(ProcMacroServer { process: Arc::new(process), path: process_path.to_owned() }) + ) -> io::Result<ProcMacroClient> { + let process = ProcMacroServerProcess::run(process_path, env)?; + Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } - pub fn path(&self) -> &AbsPath { + pub fn server_path(&self) -> &AbsPath { &self.path } + /// Loads a proc-macro dylib into the server process returning a list of `ProcMacro`s loaded. pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> { let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered(); let macros = self.process.find_proc_macros(&dylib.path)?; @@ -145,14 +146,14 @@ impl ProcMacro { pub fn expand( &self, - subtree: &tt::Subtree<Span>, - attr: Option<&tt::Subtree<Span>>, + subtree: tt::SubtreeView<'_, Span>, + attr: Option<tt::SubtreeView<'_, Span>>, env: Vec<(String, String)>, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option<String>, - ) -> Result<Result<tt::Subtree<Span>, PanicMessage>, ServerError> { + ) -> Result<Result<tt::TopSubtree<Span>, PanicMessage>, ServerError> { let version = self.process.version(); let mut span_data_table = SpanDataIndexMap::default(); @@ -160,7 +161,7 @@ impl ProcMacro { let call_site = span_data_table.insert_full(call_site).0; let mixed_site = span_data_table.insert_full(mixed_site).0; let task = ExpandMacro { - data: msg::ExpandMacroData { + data: ExpandMacroData { macro_body: FlatTree::new(subtree, version, &mut span_data_table), macro_name: self.name.to_string(), attributes: attr @@ -182,13 +183,13 @@ impl ProcMacro { current_dir, }; - let response = self.process.send_task(msg::Request::ExpandMacro(Box::new(task)))?; + let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?; match response { - msg::Response::ExpandMacro(it) => { + Response::ExpandMacro(it) => { Ok(it.map(|tree| FlatTree::to_subtree_resolved(tree, version, &span_data_table))) } - msg::Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { + Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { FlatTree::to_subtree_resolved( resp.tree, version, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 4045e25fdf1..d998b23d3bb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -11,13 +11,18 @@ use paths::AbsPath; use stdx::JodChild; use crate::{ - json::{read_json, write_json}, - msg::{Message, Request, Response, SpanMode, CURRENT_API_VERSION, RUST_ANALYZER_SPAN_SUPPORT}, + legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + Message, Request, Response, ServerConfig, SpanMode, CURRENT_API_VERSION, + RUST_ANALYZER_SPAN_SUPPORT, + }, + }, ProcMacroKind, ServerError, }; #[derive(Debug)] -pub(crate) struct ProcMacroProcessSrv { +pub(crate) struct ProcMacroServerProcess { /// The state of the proc-macro server process, the protocol is currently strictly sequential /// hence the lock on the state. state: Mutex<ProcessSrvState>, @@ -34,24 +39,24 @@ struct ProcessSrvState { stdout: BufReader<ChildStdout>, } -impl ProcMacroProcessSrv { +impl ProcMacroServerProcess { pub(crate) fn run( process_path: &AbsPath, env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)> + Clone, - ) -> io::Result<ProcMacroProcessSrv> { - let create_srv = |null_stderr| { - let mut process = Process::run(process_path, env.clone(), null_stderr)?; + ) -> io::Result<ProcMacroServerProcess> { + let create_srv = || { + let mut process = Process::run(process_path, env.clone())?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); - io::Result::Ok(ProcMacroProcessSrv { + io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, mode: SpanMode::Id, exited: OnceLock::new(), }) }; - let mut srv = create_srv(true)?; + let mut srv = create_srv()?; tracing::info!("sending proc-macro server version check"); match srv.version_check() { Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new( @@ -62,7 +67,6 @@ impl ProcMacroProcessSrv { )), Ok(v) => { tracing::info!("Proc-macro server version: {v}"); - srv = create_srv(false)?; srv.version = v; if srv.version >= RUST_ANALYZER_SPAN_SUPPORT { if let Ok(mode) = srv.enable_rust_analyzer_spans() { @@ -73,8 +77,10 @@ impl ProcMacroProcessSrv { Ok(srv) } Err(e) => { - tracing::info!(%e, "proc-macro version check failed, restarting and assuming version 0"); - create_srv(false) + tracing::info!(%e, "proc-macro version check failed"); + Err( + io::Error::new(io::ErrorKind::Other, format!("proc-macro server version check failed: {e}")), + ) } } } @@ -98,13 +104,11 @@ impl ProcMacroProcessSrv { } fn enable_rust_analyzer_spans(&self) -> Result<SpanMode, ServerError> { - let request = Request::SetConfig(crate::msg::ServerConfig { - span_mode: crate::msg::SpanMode::RustAnalyzer, - }); + let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer }); let response = self.send_task(request)?; match response { - Response::SetConfig(crate::msg::ServerConfig { span_mode }) => Ok(span_mode), + Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), } } @@ -182,9 +186,8 @@ impl Process { fn run( path: &AbsPath, env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>, - null_stderr: bool, ) -> io::Result<Process> { - let child = JodChild(mk_child(path, env, null_stderr)?); + let child = JodChild(mk_child(path, env)?); Ok(Process { child }) } @@ -200,14 +203,14 @@ impl Process { fn mk_child( path: &AbsPath, env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>, - null_stderr: bool, ) -> io::Result<Child> { + #[allow(clippy::disallowed_methods)] let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); + .stderr(Stdio::inherit()); if cfg!(windows) { let mut path_var = std::ffi::OsString::new(); path_var.push(path.parent().unwrap().parent().unwrap()); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 1c394513c45..57a28b00365 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true +tt.workspace = true [features] sysroot-abi = ["proc-macro-srv/sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 137efd5e7a0..de59e88aac4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -6,7 +6,10 @@ #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -use std::io; +#[cfg(any(feature = "sysroot-abi", rust_analyzer))] +mod main_loop; +#[cfg(any(feature = "sysroot-abi", rust_analyzer))] +use main_loop::run; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -22,57 +25,10 @@ fn main() -> std::io::Result<()> { } #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))] -fn run() -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Unsupported, +fn run() -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" .to_owned(), )) } - -#[cfg(any(feature = "sysroot-abi", rust_analyzer))] -fn run() -> io::Result<()> { - use proc_macro_api::{ - json::{read_json, write_json}, - msg::{self, Message}, - }; - use proc_macro_srv::EnvSnapshot; - - let read_request = - |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); - - let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); - - let env = EnvSnapshot::default(); - let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); - let mut buf = String::new(); - - while let Some(req) = read_request(&mut buf)? { - let res = match req { - msg::Request::ListMacros { dylib_path } => { - msg::Response::ListMacros(srv.list_macros(&dylib_path)) - } - msg::Request::ExpandMacro(task) => match srv.span_mode() { - msg::SpanMode::Id => { - msg::Response::ExpandMacro(srv.expand(*task).map(|(it, _)| it)) - } - msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended( - srv.expand(*task).map(|(tree, span_data_table)| msg::ExpandMacroExtended { - tree, - span_data_table, - }), - ), - }, - msg::Request::ApiVersionCheck {} => { - msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) - } - msg::Request::SetConfig(config) => { - srv.set_span_mode(config.span_mode); - msg::Response::SetConfig(config) - } - }; - write_response(res)? - } - - Ok(()) -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs new file mode 100644 index 00000000000..ba1fcd8e336 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -0,0 +1,134 @@ +//! The main loop of the proc-macro server. +use std::io; + +use proc_macro_api::legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpandMacroData, + ExpnGlobals, Message, SpanMode, TokenId, CURRENT_API_VERSION, + }, +}; +use proc_macro_srv::EnvSnapshot; + +pub(crate) fn run() -> io::Result<()> { + fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { + match kind { + proc_macro_srv::ProcMacroKind::CustomDerive => { + proc_macro_api::ProcMacroKind::CustomDerive + } + proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang, + proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr, + } + } + + let read_request = + |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); + + let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); + + let env = EnvSnapshot::default(); + let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); + let mut buf = String::new(); + + let mut span_mode = SpanMode::Id; + + while let Some(req) = read_request(&mut buf)? { + let res = match req { + msg::Request::ListMacros { dylib_path } => { + msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { + macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect() + })) + } + msg::Request::ExpandMacro(task) => { + let msg::ExpandMacro { + lib, + env, + current_dir, + data: + ExpandMacroData { + macro_body, + macro_name, + attributes, + has_global_spans: + ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, + span_data_table, + }, + } = *task; + match span_mode { + SpanMode::Id => msg::Response::ExpandMacro({ + let def_site = TokenId(def_site as u32); + let call_site = TokenId(call_site as u32); + let mixed_site = TokenId(mixed_site as u32); + + let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION); + let attributes = + attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); + + srv.expand( + lib, + env, + current_dir, + macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + msg::FlatTree::new_raw(tt::SubtreeView::new(&it), CURRENT_API_VERSION) + }) + .map_err(msg::PanicMessage) + }), + SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ + let mut span_data_table = deserialize_span_data_index_map(&span_data_table); + + let def_site = span_data_table[def_site]; + let call_site = span_data_table[call_site]; + let mixed_site = span_data_table[mixed_site]; + + let macro_body = + macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); + let attributes = attributes.map(|it| { + it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) + }); + srv.expand( + lib, + env, + current_dir, + macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + ( + msg::FlatTree::new( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + &mut span_data_table, + ), + serialize_span_data_index_map(&span_data_table), + ) + }) + .map(|(tree, span_data_table)| msg::ExpandMacroExtended { + tree, + span_data_table, + }) + .map_err(msg::PanicMessage) + }), + } + } + msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION), + msg::Request::SetConfig(config) => { + span_mode = config.span_mode; + msg::Response::SetConfig(config) + } + }; + write_response(res)? + } + + Ok(()) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 98385969459..00695c54737 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -23,7 +23,6 @@ syntax-bridge.workspace = true paths.workspace = true # span = {workspace = true, default-features = false} does not work span = { path = "../span", version = "0.0.0", default-features = false} -proc-macro-api.workspace = true intern.workspace = true ra-ap-rustc_lexer.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs index 9a17cfc9f36..07a10aaae57 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs @@ -7,6 +7,7 @@ fn main() { println!("cargo::rustc-check-cfg=cfg(rust_analyzer)"); let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set"); + #[allow(clippy::disallowed_methods)] let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run"); let version_string = std::str::from_utf8(&output.stdout[..]) .expect("rustc --version output must be UTF-8") diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index ff2f5d18639..d3d58a6df01 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -7,6 +7,8 @@ //! a specific rustup toolchain: this allows testing against older ABIs (e.g. //! 1.58) and future ABIs (stage1, nightly) +#![allow(clippy::disallowed_methods)] + use std::{ env, path::{Path, PathBuf}, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 26f6af84aae..fe15d42b4e4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -8,39 +8,8 @@ use std::{fmt, fs, io, time::SystemTime}; use libloading::Library; use object::Object; use paths::{Utf8Path, Utf8PathBuf}; -use proc_macro_api::ProcMacroKind; -use crate::ProcMacroSrvSpan; - -const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; - -fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, e) -} - -fn is_derive_registrar_symbol(symbol: &str) -> bool { - symbol.contains(NEW_REGISTRAR_SYMBOL) -} - -fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result<Option<String>> { - Ok(obj - .exports()? - .into_iter() - .map(|export| export.name()) - .filter_map(|sym| String::from_utf8(sym.into()).ok()) - .find(|sym| is_derive_registrar_symbol(sym)) - .map(|sym| { - // From MacOS docs: - // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html - // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be - // prepended with an underscore. - if cfg!(target_os = "macos") && sym.starts_with('_') { - sym[1..].to_owned() - } else { - sym - } - })) -} +use crate::{proc_macros::ProcMacros, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan}; /// Loads dynamic library in platform dependent manner. /// @@ -100,13 +69,14 @@ impl From<libloading::Error> for LoadProcMacroDylibError { } } -struct ProcMacroLibraryLibloading { +struct ProcMacroLibrary { + // 'static is actually the lifetime of library, so make sure this drops before _lib + proc_macros: &'static ProcMacros, // Hold on to the library so it doesn't unload _lib: Library, - proc_macros: crate::proc_macros::ProcMacros, } -impl ProcMacroLibraryLibloading { +impl ProcMacroLibrary { fn open(path: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> { let file = fs::File::open(path)?; let file = unsafe { memmap2::Mmap::map(&file) }?; @@ -119,27 +89,22 @@ impl ProcMacroLibraryLibloading { })?; let lib = load_library(path).map_err(invalid_data_err)?; - let proc_macros = crate::proc_macros::ProcMacros::from_lib( - &lib, - symbol_name, - &version_info.version_string, - )?; - Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros }) - } -} - -struct RemoveFileOnDrop(Utf8PathBuf); -impl Drop for RemoveFileOnDrop { - fn drop(&mut self) { - #[cfg(windows)] - std::fs::remove_file(&self.0).unwrap(); - _ = self.0; + let proc_macros = unsafe { + // SAFETY: We extend the lifetime here to avoid referential borrow problems + // We never reveal proc_macros to the outside and drop it before _lib + std::mem::transmute::<&ProcMacros, &'static ProcMacros>(ProcMacros::from_lib( + &lib, + symbol_name, + &version_info.version_string, + )?) + }; + Ok(ProcMacroLibrary { _lib: lib, proc_macros }) } } // Drop order matters as we can't remove the dylib before the library is unloaded pub(crate) struct Expander { - inner: ProcMacroLibraryLibloading, + inner: ProcMacroLibrary, _remove_on_drop: RemoveFileOnDrop, modified_time: SystemTime, } @@ -152,7 +117,7 @@ impl Expander { let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?; let path = ensure_file_with_lock_free_access(&lib)?; - let library = ProcMacroLibraryLibloading::open(path.as_ref())?; + let library = ProcMacroLibrary::open(path.as_ref())?; Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time }) } @@ -160,12 +125,12 @@ impl Expander { pub(crate) fn expand<S: ProcMacroSrvSpan>( &self, macro_name: &str, - macro_body: tt::Subtree<S>, - attributes: Option<tt::Subtree<S>>, + macro_body: TopSubtree<S>, + attributes: Option<TopSubtree<S>>, def_site: S, call_site: S, mixed_site: S, - ) -> Result<tt::Subtree<S>, String> + ) -> Result<TopSubtree<S>, String> where <S::Server as bridge::server::Types>::TokenStream: Default, { @@ -185,6 +150,44 @@ impl Expander { } } +fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, e) +} + +fn is_derive_registrar_symbol(symbol: &str) -> bool { + const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; + symbol.contains(NEW_REGISTRAR_SYMBOL) +} + +fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result<Option<String>> { + Ok(obj + .exports()? + .into_iter() + .map(|export| export.name()) + .filter_map(|sym| String::from_utf8(sym.into()).ok()) + .find(|sym| is_derive_registrar_symbol(sym)) + .map(|sym| { + // From MacOS docs: + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html + // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be + // prepended with an underscore. + if cfg!(target_os = "macos") && sym.starts_with('_') { + sym[1..].to_owned() + } else { + sym + } + })) +} + +struct RemoveFileOnDrop(Utf8PathBuf); +impl Drop for RemoveFileOnDrop { + fn drop(&mut self) { + #[cfg(windows)] + std::fs::remove_file(&self.0).unwrap(); + _ = self.0; + } +} + /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 85833dab1b0..7ae75713ebf 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -14,6 +14,7 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![allow(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![deny(deprecated_safe)] extern crate proc_macro; #[cfg(feature = "in-rust-tree")] @@ -38,62 +39,82 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use proc_macro_api::{ - msg::{ - self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, - SpanMode, TokenId, CURRENT_API_VERSION, - }, - ProcMacroKind, -}; -use span::Span; +use span::{Span, TokenId}; use crate::server_impl::TokenStream; +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum ProcMacroKind { + CustomDerive, + Attr, + Bang, +} + pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION"); pub struct ProcMacroSrv<'env> { expanders: HashMap<Utf8PathBuf, dylib::Expander>, - span_mode: SpanMode, env: &'env EnvSnapshot, } impl<'env> ProcMacroSrv<'env> { pub fn new(env: &'env EnvSnapshot) -> Self { - Self { expanders: Default::default(), span_mode: Default::default(), env } + Self { expanders: Default::default(), env } } } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv<'_> { - pub fn set_span_mode(&mut self, span_mode: SpanMode) { - self.span_mode = span_mode; - } - - pub fn span_mode(&self) -> SpanMode { - self.span_mode - } - - pub fn expand( + pub fn expand<S: ProcMacroSrvSpan>( &mut self, - msg::ExpandMacro { lib, env, current_dir, data }: msg::ExpandMacro, - ) -> Result<(msg::FlatTree, Vec<u32>), msg::PanicMessage> { - let span_mode = self.span_mode; + lib: impl AsRef<Utf8Path>, + env: Vec<(String, String)>, + current_dir: Option<impl AsRef<Path>>, + macro_name: String, + macro_body: tt::TopSubtree<S>, + attribute: Option<tt::TopSubtree<S>>, + def_site: S, + call_site: S, + mixed_site: S, + ) -> Result<Vec<tt::TokenTree<S>>, String> { let snapped_env = self.env; - let expander = self - .expander(lib.as_ref()) - .map_err(|err| msg::PanicMessage(format!("failed to load macro: {err}")))?; + let expander = + self.expander(lib.as_ref()).map_err(|err| format!("failed to load macro: {err}"))?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); - let result = match span_mode { - SpanMode::Id => expand_id(data, expander).map(|it| (it, vec![])), - SpanMode::RustAnalyzer => expand_ra_span(data, expander), - }; - + // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this + // includes the proc-macro symbol interner) + let result = thread::scope(|s| { + let thread = thread::Builder::new() + .stack_size(EXPANDER_STACK_SIZE) + .name(macro_name.clone()) + .spawn_scoped(s, move || { + expander + .expand( + ¯o_name, + server_impl::TopSubtree(macro_body.0.into_vec()), + attribute.map(|it| server_impl::TopSubtree(it.0.into_vec())), + def_site, + call_site, + mixed_site, + ) + .map(|tt| tt.0) + }); + let res = match thread { + Ok(handle) => handle.join(), + Err(e) => return Err(e.to_string()), + }; + + match res { + Ok(res) => res, + Err(e) => std::panic::resume_unwind(e), + } + }); prev_env.rollback(); - result.map_err(msg::PanicMessage) + result } pub fn list_macros( @@ -123,7 +144,7 @@ impl ProcMacroSrv<'_> { } } -trait ProcMacroSrvSpan: Copy { +pub trait ProcMacroSrvSpan: Copy + Send { type Server: proc_macro::bridge::server::Server<TokenStream = TokenStream<Self>>; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } @@ -147,93 +168,6 @@ impl ProcMacroSrvSpan for Span { } } } - -fn expand_id( - msg::ExpandMacroData { - macro_body, - macro_name, - attributes, - has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, - span_data_table: _, - }: msg::ExpandMacroData, - expander: &dylib::Expander, -) -> Result<msg::FlatTree, String> { - let def_site = TokenId(def_site as u32); - let call_site = TokenId(call_site as u32); - let mixed_site = TokenId(mixed_site as u32); - - let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION); - let attributes = attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); - let result = thread::scope(|s| { - let thread = thread::Builder::new() - .stack_size(EXPANDER_STACK_SIZE) - .name(macro_name.clone()) - .spawn_scoped(s, || { - expander - .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site) - .map(|it| msg::FlatTree::new_raw(&it, CURRENT_API_VERSION)) - }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => std::panic::resume_unwind(Box::new(e)), - }; - - match res { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), - } - }); - result -} - -fn expand_ra_span( - msg::ExpandMacroData { - macro_body, - macro_name, - attributes, - has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, - span_data_table, - }: msg::ExpandMacroData, - expander: &dylib::Expander, -) -> Result<(msg::FlatTree, Vec<u32>), String> { - let mut span_data_table = deserialize_span_data_index_map(&span_data_table); - - let def_site = span_data_table[def_site]; - let call_site = span_data_table[call_site]; - let mixed_site = span_data_table[mixed_site]; - - let macro_body = macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); - let attributes = - attributes.map(|it| it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table)); - // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this - // includes the proc-macro symbol interner) - let result = thread::scope(|s| { - let thread = thread::Builder::new() - .stack_size(EXPANDER_STACK_SIZE) - .name(macro_name.clone()) - .spawn_scoped(s, || { - expander - .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site) - .map(|it| { - ( - msg::FlatTree::new(&it, CURRENT_API_VERSION, &mut span_data_table), - serialize_span_data_index_map(&span_data_table), - ) - }) - }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => std::panic::resume_unwind(Box::new(e)), - }; - - match res { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), - } - }); - result -} - pub struct PanicMessage { message: Option<String>, } @@ -254,10 +188,13 @@ impl Default for EnvSnapshot { } } +static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + struct EnvChange<'snap> { changed_vars: Vec<String>, prev_working_dir: Option<PathBuf>, snap: &'snap EnvSnapshot, + _guard: std::sync::MutexGuard<'snap, ()>, } impl<'snap> EnvChange<'snap> { @@ -266,6 +203,7 @@ impl<'snap> EnvChange<'snap> { new_vars: Vec<(String, String)>, current_dir: Option<&Path>, ) -> EnvChange<'snap> { + let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let prev_working_dir = match current_dir { Some(dir) => { let prev_working_dir = std::env::current_dir().ok(); @@ -284,11 +222,13 @@ impl<'snap> EnvChange<'snap> { changed_vars: new_vars .into_iter() .map(|(k, v)| { - env::set_var(&k, v); + // SAFETY: We have acquired the environment lock + unsafe { env::set_var(&k, v) }; k }) .collect(), prev_working_dir, + _guard: guard, } } @@ -298,9 +238,12 @@ impl<'snap> EnvChange<'snap> { impl Drop for EnvChange<'_> { fn drop(&mut self) { for name in self.changed_vars.drain(..) { - match self.snap.vars.get::<std::ffi::OsStr>(name.as_ref()) { - Some(prev_val) => env::set_var(name, prev_val), - None => env::remove_var(name), + // SAFETY: We have acquired the environment lock + unsafe { + match self.snap.vars.get::<std::ffi::OsStr>(name.as_ref()) { + Some(prev_val) => env::set_var(name, prev_val), + None => env::remove_var(name), + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs index 097b39a3f91..58f5e80dc4e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs @@ -1,15 +1,15 @@ //! Proc macro ABI use proc_macro::bridge; -use proc_macro_api::ProcMacroKind; use libloading::Library; -use crate::{dylib::LoadProcMacroDylibError, ProcMacroSrvSpan}; +use crate::{ + dylib::LoadProcMacroDylibError, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan, +}; -pub(crate) struct ProcMacros { - exported_macros: Vec<bridge::client::ProcMacro>, -} +#[repr(transparent)] +pub(crate) struct ProcMacros([bridge::client::ProcMacro]); impl From<bridge::PanicMessage> for crate::PanicMessage { fn from(p: bridge::PanicMessage) -> Self { @@ -27,29 +27,28 @@ impl ProcMacros { /// *`info` - RustCInfo about the compiler that was used to compile the /// macro crate. This is the information we use to figure out /// which ABI to return - pub(crate) fn from_lib( - lib: &Library, + pub(crate) fn from_lib<'l>( + lib: &'l Library, symbol_name: String, version_string: &str, - ) -> Result<ProcMacros, LoadProcMacroDylibError> { - if version_string == crate::RUSTC_VERSION_STRING { - let macros = - unsafe { lib.get::<&&[bridge::client::ProcMacro]>(symbol_name.as_bytes()) }?; - - return Ok(Self { exported_macros: macros.to_vec() }); + ) -> Result<&'l ProcMacros, LoadProcMacroDylibError> { + if version_string != crate::RUSTC_VERSION_STRING { + return Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned())); } - Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned())) + unsafe { lib.get::<&'l &'l ProcMacros>(symbol_name.as_bytes()) } + .map(|it| **it) + .map_err(Into::into) } pub(crate) fn expand<S: ProcMacroSrvSpan>( &self, macro_name: &str, - macro_body: tt::Subtree<S>, - attributes: Option<tt::Subtree<S>>, + macro_body: TopSubtree<S>, + attributes: Option<TopSubtree<S>>, def_site: S, call_site: S, mixed_site: S, - ) -> Result<tt::Subtree<S>, crate::PanicMessage> { + ) -> Result<TopSubtree<S>, crate::PanicMessage> { let parsed_body = crate::server_impl::TokenStream::with_subtree(macro_body); let parsed_attributes = attributes @@ -57,7 +56,7 @@ impl ProcMacros { crate::server_impl::TokenStream::with_subtree(attr) }); - for proc_macro in &self.exported_macros { + for proc_macro in &self.0 { match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } if *trait_name == macro_name => @@ -103,7 +102,7 @@ impl ProcMacros { } pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.exported_macros + self.0 .iter() .map(|proc_macro| match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs index c9a86216905..3d999421794 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs @@ -8,6 +8,8 @@ //! //! FIXME: No span and source file information is implemented yet +use std::fmt; + use proc_macro::bridge; mod token_stream; @@ -19,6 +21,32 @@ pub mod token_id; // pub use symbol::*; use tt::Spacing; +#[derive(Clone)] +pub(crate) struct TopSubtree<S>(pub(crate) Vec<tt::TokenTree<S>>); + +impl<S: Copy + fmt::Debug> fmt::Debug for TopSubtree<S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&tt::TokenTreesView::new(&self.0), f) + } +} + +impl<S: Copy> TopSubtree<S> { + pub(crate) fn top_subtree(&self) -> &tt::Subtree<S> { + let tt::TokenTree::Subtree(subtree) = &self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + subtree + } + + pub(crate) fn from_bridge(group: bridge::Group<TokenStream<S>, S>) -> Self { + let delimiter = delim_to_internal(group.delimiter, group.span); + let mut tts = + group.stream.map(|it| it.token_trees).unwrap_or_else(|| Vec::with_capacity(1)); + tts.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: tts.len() as u32 })); + TopSubtree(tts) + } +} + fn delim_to_internal<S>(d: proc_macro::Delimiter, span: bridge::DelimSpan<S>) -> tt::Delimiter<S> { let kind = match d { proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 1b535d2a1cc..beaebf33300 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -6,23 +6,18 @@ //! change their representation to be compatible with rust-analyzer's. use std::{ collections::{HashMap, HashSet}, - iter, ops::{Bound, Range}, }; use intern::Symbol; use proc_macro::bridge::{self, server}; -use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; +use span::{FileId, Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; use tt::{TextRange, TextSize}; -use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, -}; +use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree}; mod tt { pub use tt::*; - pub type Subtree = ::tt::Subtree<super::Span>; pub type TokenTree = ::tt::TokenTree<super::Span>; pub type Leaf = ::tt::Leaf<super::Span>; pub type Literal = ::tt::Literal<super::Span>; @@ -32,8 +27,10 @@ mod tt { type TokenStream = crate::server_impl::TokenStream<Span>; -#[derive(Clone)] -pub struct SourceFile; +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct SourceFile { + file_id: FileId, +} pub struct FreeFunctions; pub struct RaSpanServer { @@ -159,15 +156,8 @@ impl server::TokenStream for RaSpanServer { ) -> Self::TokenStream { match tree { bridge::TokenTree::Group(group) => { - let group = tt::Subtree { - delimiter: delim_to_internal(group.delimiter, group.span), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Box::new([]), - }, - }; - let tree = tt::TokenTree::from(group); - Self::TokenStream::from_iter(iter::once(tree)) + let group = TopSubtree::from_bridge(group); + TokenStream { token_trees: group.0 } } bridge::TokenTree::Ident(ident) => { @@ -179,7 +169,7 @@ impl server::TokenStream for RaSpanServer { }; let leaf = tt::Leaf::from(ident); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Literal(literal) => { @@ -192,7 +182,7 @@ impl server::TokenStream for RaSpanServer { let leaf: tt::Leaf = tt::Leaf::from(literal); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Punct(p) => { @@ -203,7 +193,7 @@ impl server::TokenStream for RaSpanServer { }; let leaf = tt::Leaf::from(punct); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } } } @@ -251,49 +241,13 @@ impl server::TokenStream for RaSpanServer { &mut self, stream: Self::TokenStream, ) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(bridge::Ident { - sym: ident.sym, - is_raw: ident.is_raw.yes(), - span: ident.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - bridge::TokenTree::Literal(bridge::Literal { - span: lit.span, - kind: literal_kind_to_external(lit.kind), - symbol: lit.symbol, - suffix: lit.suffix, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == tt::Spacing::Joint, - span: punct.span, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(subtree.token_trees.into_vec().into_iter().collect()) - }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), - }), - }) - .collect() + stream.into_bridge() } } impl server::SourceFile for RaSpanServer { - fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { - // FIXME - true + fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { + file1 == file2 } fn path(&mut self, _file: &Self::SourceFile) -> String { // FIXME @@ -308,9 +262,8 @@ impl server::Span for RaSpanServer { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span) } - fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { - // FIXME stub, requires db - SourceFile {} + fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { + SourceFile { file_id: span.anchor.file_id.file_id() } } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools @@ -507,13 +460,14 @@ mod tests { close: span, kind: tt::DelimiterKind::Brace, }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, - symbol: Symbol::intern("string"), - suffix: None, - span, - }))]), + len: 1, }), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, + symbol: Symbol::intern("string"), + suffix: None, + span, + })), ], }; @@ -530,35 +484,38 @@ mod tests { }, ctx: SyntaxContextId::ROOT, }; - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: span, - close: span, - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + let subtree_paren_a = vec![ + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: span, + close: span, + kind: tt::DelimiterKind::Parenthesis, + }, + len: 1, + }), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, sym: Symbol::intern("a"), span, - }))]), - }); + })), + ]; let t1 = TokenStream::from_str("(a)", span).unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); + assert_eq!(t1.token_trees.len(), 2); + assert!(t1.token_trees == subtree_paren_a); let t2 = TokenStream::from_str("(a);", span).unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); + assert_eq!(t2.token_trees.len(), 3); + assert!(t2.token_trees[0..2] == subtree_paren_a); let underscore = TokenStream::from_str("_", span).unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern("_"), - span, - is_raw: tt::IdentIsRaw::No, - })) + assert!( + underscore.token_trees[0] + == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("_"), + span, + is_raw: tt::IdentIsRaw::No, + })) ); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index e478b1c853b..466eb14b55e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -1,30 +1,22 @@ //! proc-macro server backend based on [`proc_macro_api::msg::TokenId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - iter, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use proc_macro::bridge::{self, server}; -use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, -}; +use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree}; mod tt { - pub use proc_macro_api::msg::TokenId; + pub use span::TokenId; pub use tt::*; - pub type Subtree = ::tt::Subtree<TokenId>; pub type TokenTree = ::tt::TokenTree<TokenId>; pub type Leaf = ::tt::Leaf<TokenId>; pub type Literal = ::tt::Literal<TokenId>; pub type Punct = ::tt::Punct<TokenId>; pub type Ident = ::tt::Ident<TokenId>; } -type Group = tt::Subtree; type TokenTree = tt::TokenTree; type Punct = tt::Punct; type Spacing = tt::Spacing; @@ -148,15 +140,8 @@ impl server::TokenStream for TokenIdServer { ) -> Self::TokenStream { match tree { bridge::TokenTree::Group(group) => { - let group = Group { - delimiter: delim_to_internal(group.delimiter, group.span), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Box::new([]), - }, - }; - let tree = TokenTree::from(group); - Self::TokenStream::from_iter(iter::once(tree)) + let group = TopSubtree::from_bridge(group); + TokenStream { token_trees: group.0 } } bridge::TokenTree::Ident(ident) => { @@ -167,7 +152,7 @@ impl server::TokenStream for TokenIdServer { }; let leaf = tt::Leaf::from(ident); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Literal(literal) => { @@ -180,7 +165,7 @@ impl server::TokenStream for TokenIdServer { let leaf = tt::Leaf::from(literal); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Punct(p) => { @@ -191,7 +176,7 @@ impl server::TokenStream for TokenIdServer { }; let leaf = tt::Leaf::from(punct); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } } } @@ -234,42 +219,7 @@ impl server::TokenStream for TokenIdServer { &mut self, stream: Self::TokenStream, ) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(bridge::Ident { - sym: ident.sym, - is_raw: ident.is_raw.yes(), - span: ident.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - bridge::TokenTree::Literal(bridge::Literal { - span: lit.span, - kind: literal_kind_to_external(lit.kind), - symbol: lit.symbol, - suffix: lit.suffix, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == Spacing::Joint, - span: punct.span, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(TokenStream { token_trees: subtree.token_trees.into_vec() }) - }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), - }), - }) - .collect() + stream.into_bridge() } } @@ -398,7 +348,7 @@ mod tests { close: tt::TokenId(0), kind: tt::DelimiterKind::Brace, }, - token_trees: Box::new([]), + len: 0, }), ], }; @@ -408,35 +358,38 @@ mod tests { #[test] fn test_ra_server_from_str() { - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId(0), - close: tt::TokenId(0), - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + let subtree_paren_a = vec![ + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: tt::TokenId(0), + close: tt::TokenId(0), + kind: tt::DelimiterKind::Parenthesis, + }, + len: 1, + }), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, sym: Symbol::intern("a"), span: tt::TokenId(0), - }))]), - }); + })), + ]; let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); + assert_eq!(t1.token_trees.len(), 2); + assert!(t1.token_trees[0..2] == subtree_paren_a); let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); + assert_eq!(t2.token_trees.len(), 3); + assert!(t2.token_trees[0..2] == subtree_paren_a); let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern("_"), - span: tt::TokenId(0), - is_raw: tt::IdentIsRaw::No, - })) + assert!( + underscore.token_trees[0] + == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("_"), + span: tt::TokenId(0), + is_raw: tt::IdentIsRaw::No, + })) ); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index 5649e60e0bb..645f7e7c59a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -1,10 +1,20 @@ //! TokenStream implementation used by sysroot ABI -use tt::TokenTree; +use proc_macro::bridge; -#[derive(Debug, Clone)] +use crate::server_impl::{delim_to_external, literal_kind_to_external, TopSubtree}; + +#[derive(Clone)] pub struct TokenStream<S> { - pub(super) token_trees: Vec<TokenTree<S>>, + pub(super) token_trees: Vec<tt::TokenTree<S>>, +} + +impl<S: std::fmt::Debug + Copy> std::fmt::Debug for TokenStream<S> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TokenStream") + .field("token_trees", &tt::TokenTreesView::new(&self.token_trees)) + .finish() + } } impl<S> Default for TokenStream<S> { @@ -13,84 +23,85 @@ impl<S> Default for TokenStream<S> { } } -impl<S> TokenStream<S> { +impl<S: Copy> TokenStream<S> { pub(crate) fn new() -> Self { TokenStream::default() } - pub(crate) fn with_subtree(subtree: tt::Subtree<S>) -> Self { - if subtree.delimiter.kind != tt::DelimiterKind::Invisible { - TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } - } else { - TokenStream { token_trees: subtree.token_trees.into_vec() } + pub(crate) fn with_subtree(subtree: TopSubtree<S>) -> Self { + let delimiter_kind = subtree.top_subtree().delimiter.kind; + let mut token_trees = subtree.0; + if delimiter_kind == tt::DelimiterKind::Invisible { + token_trees.remove(0); } + TokenStream { token_trees } } - pub(crate) fn into_subtree(self, call_site: S) -> tt::Subtree<S> + pub(crate) fn into_subtree(mut self, call_site: S) -> TopSubtree<S> where S: Copy, { - tt::Subtree { - delimiter: tt::Delimiter { - open: call_site, - close: call_site, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: self.token_trees.into_boxed_slice(), - } + self.token_trees.insert( + 0, + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: call_site, + close: call_site, + kind: tt::DelimiterKind::Invisible, + }, + len: self.token_trees.len() as u32, + }), + ); + TopSubtree(self.token_trees) } pub(super) fn is_empty(&self) -> bool { self.token_trees.is_empty() } -} -/// Creates a token stream containing a single token tree. -impl<S> From<TokenTree<S>> for TokenStream<S> { - fn from(tree: TokenTree<S>) -> TokenStream<S> { - TokenStream { token_trees: vec![tree] } - } -} - -/// Collects a number of token trees into a single stream. -impl<S> FromIterator<TokenTree<S>> for TokenStream<S> { - fn from_iter<I: IntoIterator<Item = TokenTree<S>>>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl<S> FromIterator<TokenStream<S>> for TokenStream<S> { - fn from_iter<I: IntoIterator<Item = TokenStream<S>>>(streams: I) -> Self { - let mut builder = TokenStreamBuilder::new(); - streams.into_iter().for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl<S> Extend<TokenTree<S>> for TokenStream<S> { - fn extend<I: IntoIterator<Item = TokenTree<S>>>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl<S> Extend<TokenStream<S>> for TokenStream<S> { - fn extend<I: IntoIterator<Item = TokenStream<S>>>(&mut self, streams: I) { - for item in streams { - for tkn in item { - match tkn { - tt::TokenTree::Subtree(subtree) - if subtree.delimiter.kind == tt::DelimiterKind::Invisible => - { - self.token_trees.extend(subtree.token_trees.into_vec().into_iter()); - } - _ => { - self.token_trees.push(tkn); - } + pub(crate) fn into_bridge(self) -> Vec<bridge::TokenTree<Self, S, intern::Symbol>> { + let mut result = Vec::new(); + let mut iter = self.token_trees.into_iter(); + while let Some(tree) = iter.next() { + match tree { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { + result.push(bridge::TokenTree::Ident(bridge::Ident { + sym: ident.sym, + is_raw: ident.is_raw.yes(), + span: ident.span, + })) + } + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + result.push(bridge::TokenTree::Literal(bridge::Literal { + span: lit.span, + kind: literal_kind_to_external(lit.kind), + symbol: lit.symbol, + suffix: lit.suffix, + })) + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { + result.push(bridge::TokenTree::Punct(bridge::Punct { + ch: punct.char as u8, + joint: punct.spacing == tt::Spacing::Joint, + span: punct.span, + })) + } + tt::TokenTree::Subtree(subtree) => { + result.push(bridge::TokenTree::Group(bridge::Group { + delimiter: delim_to_external(subtree.delimiter), + stream: if subtree.len == 0 { + None + } else { + Some(TokenStream { + token_trees: iter.by_ref().take(subtree.usize_len()).collect(), + }) + }, + span: bridge::DelimSpan::from_single(subtree.delimiter.open), + })) } } } + result } } @@ -103,19 +114,7 @@ pub(super) mod token_stream_impls { use core::fmt; - use super::{TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - impl<S> IntoIterator for TokenStream<S> { - type Item = TokenTree<S>; - type IntoIter = std::vec::IntoIter<TokenTree<S>>; - - fn into_iter(self) -> Self::IntoIter { - self.token_trees.into_iter() - } - } + use super::{TokenStream, TopSubtree}; /// Attempts to break the string into tokens and parse those tokens into a token stream. /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters @@ -133,7 +132,7 @@ pub(super) mod token_stream_impls { ) .ok_or_else(|| format!("lexing error: {src}"))?; - Ok(TokenStream::with_subtree(subtree)) + Ok(TokenStream::with_subtree(TopSubtree(subtree.0.into_vec()))) } } @@ -145,13 +144,13 @@ pub(super) mod token_stream_impls { } } -impl<S> TokenStreamBuilder<S> { +impl<S: Copy> TokenStreamBuilder<S> { pub(super) fn new() -> TokenStreamBuilder<S> { TokenStreamBuilder { acc: TokenStream::new() } } pub(super) fn push(&mut self, stream: TokenStream<S>) { - self.acc.extend(stream) + self.acc.token_trees.extend(stream.token_trees) } pub(super) fn build(self) -> TokenStream<S> { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index cc5d4a89131..37d51050f32 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -1,17 +1,18 @@ //! utils used in proc-macro tests use expect_test::Expect; -use proc_macro_api::msg::TokenId; -use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId, TokenId}; use tt::TextRange; use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv}; fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream<TokenId> { - crate::server_impl::TokenStream::with_subtree( + crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree( syntax_bridge::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src) - .unwrap(), - ) + .unwrap() + .0 + .into_vec(), + )) } fn parse_string_spanned( @@ -19,9 +20,12 @@ fn parse_string_spanned( call_site: SyntaxContextId, src: &str, ) -> crate::server_impl::TokenStream<Span> { - crate::server_impl::TokenStream::with_subtree( - syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(), - ) + crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree( + syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src) + .unwrap() + .0 + .into_vec(), + )) } pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) { diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 524323b9736..b0939229f93 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -172,19 +172,18 @@ impl WorkspaceBuildScripts { } let res = (|| { let target_libdir = (|| { - let mut cargo_config = sysroot.tool(Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo, current_dir); cargo_config.envs(extra_env); cargo_config - .current_dir(current_dir) .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) .env("RUSTC_BOOTSTRAP", "1"); - if let Ok(it) = utf8_stdout(cargo_config) { + if let Ok(it) = utf8_stdout(&mut cargo_config) { return Ok(it); } - let mut cmd = sysroot.tool(Tool::Rustc); + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); cmd.envs(extra_env); cmd.args(["--print", "target-libdir"]); - utf8_stdout(cmd) + utf8_stdout(&mut cmd) })()?; let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) @@ -390,12 +389,12 @@ impl WorkspaceBuildScripts { ) -> io::Result<Command> { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { - let mut cmd = Command::new(program); + let mut cmd = toolchain::command(program, current_dir); cmd.args(args); cmd } _ => { - let mut cmd = sysroot.tool(Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo, current_dir); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); @@ -448,7 +447,6 @@ impl WorkspaceBuildScripts { } }; - cmd.current_dir(current_dir); cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index ba4946bf0b9..4d906c2aeb3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -4,6 +4,7 @@ use std::ops; use std::str::from_utf8; use anyhow::Context; +use base_db::Env; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; @@ -13,8 +14,8 @@ use serde_json::from_value; use span::Edition; use toolchain::Tool; -use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata}; use crate::{CfgOverrides, InvocationStrategy}; +use crate::{ManifestPath, Sysroot}; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. @@ -34,6 +35,8 @@ pub struct CargoWorkspace { target_directory: AbsPathBuf, manifest_path: ManifestPath, is_virtual_workspace: bool, + /// Environment variables set in the `.cargo/config` file. + config_env: Env, } impl ops::Index<Package> for CargoWorkspace { @@ -86,8 +89,6 @@ pub struct CargoConfig { pub target: Option<String>, /// Sysroot loading behavior pub sysroot: Option<RustLibSource>, - /// How to query metadata for the sysroot crate. - pub sysroot_query_metadata: SysrootQueryMetadata, pub sysroot_src: Option<AbsPathBuf>, /// rustc private crate source pub rustc_source: Option<RustLibSource>, @@ -251,6 +252,18 @@ impl TargetKind { } } +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct CargoMetadataConfig { + /// List of features to activate. + pub features: CargoFeatures, + /// rustc targets + pub targets: Vec<String>, + /// Extra args to pass to the cargo command. + pub extra_args: Vec<String>, + /// Extra env vars to set when invoking the cargo command + pub extra_env: FxHashMap<String, String>, +} + // Deserialize helper for the cargo metadata #[derive(Deserialize, Default)] struct PackageMetadata { @@ -265,7 +278,7 @@ impl CargoWorkspace { pub fn fetch_metadata( cargo_toml: &ManifestPath, current_dir: &AbsPath, - config: &CargoConfig, + config: &CargoMetadataConfig, sysroot: &Sysroot, locked: bool, progress: &dyn Fn(String), @@ -276,15 +289,13 @@ impl CargoWorkspace { fn fetch_metadata_( cargo_toml: &ManifestPath, current_dir: &AbsPath, - config: &CargoConfig, + config: &CargoMetadataConfig, sysroot: &Sysroot, locked: bool, no_deps: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> { - let targets = find_list_of_build_targets(config, cargo_toml, sysroot); - - let cargo = sysroot.tool(Tool::Cargo); + let cargo = sysroot.tool(Tool::Cargo, current_dir); let mut meta = MetadataCommand::new(); meta.cargo_path(cargo.get_program()); cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default())); @@ -319,12 +330,9 @@ impl CargoWorkspace { } } - if !targets.is_empty() { - other_options.append( - &mut targets - .into_iter() - .flat_map(|target| ["--filter-platform".to_owned(), target]) - .collect(), + if !config.targets.is_empty() { + other_options.extend( + config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]), ); } // The manifest is a rust file, so this means its a script manifest @@ -388,6 +396,7 @@ impl CargoWorkspace { pub fn new( mut meta: cargo_metadata::Metadata, ws_manifest_path: ManifestPath, + cargo_config_env: Env, ) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); let mut packages = Arena::default(); @@ -509,6 +518,7 @@ impl CargoWorkspace { target_directory, manifest_path: ws_manifest_path, is_virtual_workspace, + config_env: cargo_config_env, } } @@ -595,80 +605,8 @@ impl CargoWorkspace { pub fn is_virtual_workspace(&self) -> bool { self.is_virtual_workspace } -} - -fn find_list_of_build_targets( - config: &CargoConfig, - cargo_toml: &ManifestPath, - sysroot: &Sysroot, -) -> Vec<String> { - if let Some(target) = &config.target { - return [target.into()].to_vec(); - } - - let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot); - if !build_targets.is_empty() { - return build_targets; - } - - rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect() -} - -fn rustc_discover_host_triple( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap<String, String>, - sysroot: &Sysroot, -) -> Option<String> { - let mut rustc = sysroot.tool(Tool::Rustc); - rustc.envs(extra_env); - rustc.current_dir(cargo_toml.parent()).arg("-vV"); - tracing::debug!("Discovering host platform by {:?}", rustc); - match utf8_stdout(rustc) { - Ok(stdout) => { - let field = "host: "; - let target = stdout.lines().find_map(|l| l.strip_prefix(field)); - if let Some(target) = target { - Some(target.to_owned()) - } else { - // If we fail to resolve the host platform, it's not the end of the world. - tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout); - None - } - } - Err(e) => { - tracing::warn!("Failed to discover host platform: {}", e); - None - } - } -} - -fn cargo_config_build_target( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap<String, String>, - sysroot: &Sysroot, -) -> Vec<String> { - let mut cargo_config = sysroot.tool(Tool::Cargo); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["-Z", "unstable-options", "config", "get", "build.target"]) - .env("RUSTC_BOOTSTRAP", "1"); - // if successful we receive `build.target = "target-triple"` - // or `build.target = ["<target 1>", ..]` - tracing::debug!("Discovering cargo config target by {:?}", cargo_config); - utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default() -} - -fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> { - let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); - - if !trimmed.starts_with('[') { - return [trimmed.to_owned()].to_vec(); - } - let res = serde_json::from_str(trimmed); - if let Err(e) = &res { - tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e); + pub fn env(&self) -> &Env { + &self.config_env } - res.unwrap_or_default() } diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index ff9d2035f60..37fffba2955 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -1,9 +1,10 @@ //! Cargo-like environment variables injection. use base_db::Env; +use paths::Utf8Path; use rustc_hash::FxHashMap; use toolchain::Tool; -use crate::{utf8_stdout, CargoWorkspace, ManifestPath, PackageData, Sysroot, TargetKind}; +use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind}; /// Recreates the compile-time environment variables that Cargo sets. /// @@ -50,34 +51,23 @@ pub(crate) fn inject_cargo_env(env: &mut Env) { env.set("CARGO", Tool::Cargo.path().to_string()); } -pub(crate) fn inject_rustc_tool_env( - env: &mut Env, - cargo: &CargoWorkspace, - cargo_name: &str, - kind: TargetKind, -) { +pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) { _ = kind; // FIXME // if kind.is_executable() { // env.set("CARGO_BIN_NAME", cargo_name); // } env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_")); - // NOTE: Technically we should set this for all crates, but that will worsen the deduplication - // logic so for now just keeping it proc-macros ought to be fine. - if kind.is_proc_macro() { - env.set("CARGO_RUSTC_CURRENT_DIR", cargo.manifest_path().parent().to_string()); - } } pub(crate) fn cargo_config_env( manifest: &ManifestPath, extra_env: &FxHashMap<String, String>, sysroot: &Sysroot, -) -> FxHashMap<String, String> { - let mut cargo_config = sysroot.tool(Tool::Cargo); +) -> Env { + let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent()); cargo_config.envs(extra_env); cargo_config - .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); if manifest.is_rust_manifest() { @@ -85,8 +75,8 @@ pub(crate) fn cargo_config_env( } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); - utf8_stdout(cargo_config) - .map(parse_output_cargo_config_env) + utf8_stdout(&mut cargo_config) + .map(|stdout| parse_output_cargo_config_env(manifest, &stdout)) .inspect(|env| { tracing::debug!("Discovered cargo config env: {:?}", env); }) @@ -96,18 +86,61 @@ pub(crate) fn cargo_config_env( .unwrap_or_default() } -fn parse_output_cargo_config_env(stdout: String) -> FxHashMap<String, String> { - stdout - .lines() - .filter_map(|l| l.strip_prefix("env.")) - .filter_map(|l| l.split_once(" = ")) - .filter_map(|(k, v)| { - if k.contains('.') { - k.strip_suffix(".value").zip(Some(v)) - } else { - Some((k, v)) +fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env { + let mut env = Env::default(); + let mut relatives = vec![]; + for (key, val) in + stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = ")) + { + let val = val.trim_matches('"').to_owned(); + if let Some((key, modifier)) = key.split_once('.') { + match modifier { + "relative" => relatives.push((key, val)), + "value" => _ = env.insert(key, val), + _ => { + tracing::warn!( + "Unknown modifier in cargo config env: {}, expected `relative` or `value`", + modifier + ); + continue; + } } - }) - .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned())) - .collect() + } else { + env.insert(key, val); + } + } + // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest. + // But cargo does not provide this information. + let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent()); + for (key, relative) in relatives { + if relative != "true" { + continue; + } + if let Some(suffix) = env.get(key) { + env.insert(key, base.join(suffix).to_string()); + } + } + env +} + +#[test] +fn parse_output_cargo_config_env_works() { + let stdout = r#" +env.CARGO_WORKSPACE_DIR.relative = true +env.CARGO_WORKSPACE_DIR.value = "" +env.RELATIVE.relative = true +env.RELATIVE.value = "../relative" +env.INVALID.relative = invalidbool +env.INVALID.value = "../relative" +env.TEST.value = "test" +"# + .trim(); + let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(); + let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml")); + let manifest = ManifestPath::try_from(manifest).unwrap(); + let env = parse_output_cargo_config_env(&manifest, stdout); + assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); + assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str())); + assert_eq!(env.get("INVALID").as_deref(), Some("../relative")); + assert_eq!(env.get("TEST").as_deref(), Some("test")); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index da8afc5d3a1..fc1fd7b877f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -15,14 +15,32 @@ //! procedural macros). //! * Lowering of concrete model to a [`base_db::CrateGraph`] +pub mod project_json; +pub mod toolchain_info { + pub mod rustc_cfg; + pub mod target_data_layout; + pub mod target_tuple; + pub mod version; + + use std::path::Path; + + use crate::{ManifestPath, Sysroot}; + + #[derive(Copy, Clone)] + pub enum QueryConfig<'a> { + /// Directly invoke `rustc` to query the desired information. + Rustc(&'a Sysroot, &'a Path), + /// Attempt to use cargo to query the desired information, honoring cargo configurations. + /// If this fails, falls back to invoking `rustc` directly. + Cargo(&'a Sysroot, &'a ManifestPath), + } +} + mod build_dependencies; mod cargo_workspace; mod env; mod manifest_path; -pub mod project_json; -mod rustc_cfg; mod sysroot; -pub mod target_data_layout; mod workspace; #[cfg(test)] @@ -42,8 +60,8 @@ use rustc_hash::FxHashSet; pub use crate::{ build_dependencies::WorkspaceBuildScripts, cargo_workspace::{ - CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustLibSource, Target, TargetData, TargetKind, + CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, + PackageDependency, RustLibSource, Target, TargetData, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, @@ -181,7 +199,7 @@ impl fmt::Display for ProjectManifest { } } -fn utf8_stdout(mut cmd: Command) -> anyhow::Result<String> { +fn utf8_stdout(cmd: &mut Command) -> anyhow::Result<String> { let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?; if !output.status.success() { match String::from_utf8(output.stderr) { @@ -241,9 +259,20 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> { Ok(res) } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum SysrootQueryMetadata { - #[default] - CargoMetadata, - None, +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SysrootSourceWorkspaceConfig { + CargoMetadata(CargoMetadataConfig), + Stitched, +} + +impl Default for SysrootSourceWorkspaceConfig { + fn default() -> Self { + SysrootSourceWorkspaceConfig::default_cargo() + } +} + +impl SysrootSourceWorkspaceConfig { + pub fn default_cargo() -> Self { + SysrootSourceWorkspaceConfig::CargoMetadata(Default::default()) + } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs index a72dab60752..73a4e6e1216 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs @@ -46,7 +46,7 @@ impl ManifestPath { } pub fn is_rust_manifest(&self) -> bool { - self.file.extension().map_or(false, |ext| ext == "rs") + self.file.extension() == Some("rs") } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs deleted file mode 100644 index bc1f0e6fbf2..00000000000 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Runs `rustc --print cfg` to get built-in cfg flags. - -use anyhow::Context; -use cfg::CfgAtom; -use intern::Symbol; -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print cfg` is discovered and invoked. -pub(crate) enum RustcCfgConfig<'a> { - /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub(crate) fn get( - target: Option<&str>, - extra_env: &FxHashMap<String, String>, - config: RustcCfgConfig<'_>, -) -> Vec<CfgAtom> { - let _p = tracing::info_span!("rustc_cfg::get").entered(); - let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1); - - // Some nightly-only cfgs, which are required for stdlib - res.push(CfgAtom::Flag(Symbol::intern("target_thread_local"))); - for key in ["target_has_atomic", "target_has_atomic_load_store"] { - for ty in ["8", "16", "32", "64", "cas", "ptr"] { - res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) }); - } - res.push(CfgAtom::Flag(Symbol::intern(key))); - } - - let rustc_cfgs = get_rust_cfgs(target, extra_env, config); - - let rustc_cfgs = match rustc_cfgs { - Ok(cfgs) => cfgs, - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); - return res; - } - }; - - let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::<Result<Vec<_>, _>>(); - - match rustc_cfgs { - Ok(rustc_cfgs) => { - tracing::debug!(?rustc_cfgs, "rustc cfgs found"); - res.extend(rustc_cfgs); - } - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs") - } - } - - res -} - -fn get_rust_cfgs( - target: Option<&str>, - extra_env: &FxHashMap<String, String>, - config: RustcCfgConfig<'_>, -) -> anyhow::Result<String> { - let sysroot = match config { - RustcCfgConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - match utf8_stdout(cmd) { - Ok(it) => return Ok(it), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcCfgConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = sysroot.tool(Tool::Rustc); - cmd.envs(extra_env); - cmd.args(["--print", "cfg", "-O"]); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - utf8_stdout(cmd).context("unable to fetch cfgs via `rustc --print cfg -O`") -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 8426e689a64..8f633d24be9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -4,7 +4,12 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, ops, process::Command}; +use std::{ + env, fs, + ops::{self, Not}, + path::Path, + process::Command, +}; use anyhow::{format_err, Result}; use base_db::CrateName; @@ -12,20 +17,24 @@ use itertools::Itertools; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; +use stdx::format_to; use toolchain::{probe_for_binary, Tool}; -use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata}; +use crate::{ + cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath, + SysrootSourceWorkspaceConfig, +}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Sysroot { root: Option<AbsPathBuf>, src_root: Option<AbsPathBuf>, - mode: SysrootMode, + workspace: SysrootWorkspace, error: Option<String>, } #[derive(Debug, Clone, Eq, PartialEq)] -pub(crate) enum SysrootMode { +pub(crate) enum SysrootWorkspace { Workspace(CargoWorkspace), Stitched(Stitched), Empty, @@ -79,7 +88,7 @@ pub(crate) struct SysrootCrateData { impl Sysroot { pub const fn empty() -> Sysroot { - Sysroot { root: None, src_root: None, mode: SysrootMode::Empty, error: None } + Sysroot { root: None, src_root: None, workspace: SysrootWorkspace::Empty, error: None } } /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` @@ -96,10 +105,10 @@ impl Sysroot { } pub fn is_empty(&self) -> bool { - match &self.mode { - SysrootMode::Workspace(ws) => ws.packages().next().is_none(), - SysrootMode::Stitched(stitched) => stitched.crates.is_empty(), - SysrootMode::Empty => true, + match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().next().is_none(), + SysrootWorkspace::Stitched(stitched) => stitched.crates.is_empty(), + SysrootWorkspace::Empty => true, } } @@ -108,66 +117,53 @@ impl Sysroot { } pub fn num_packages(&self) -> usize { - match &self.mode { - SysrootMode::Workspace(ws) => ws.packages().count(), - SysrootMode::Stitched(c) => c.crates().count(), - SysrootMode::Empty => 0, + match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().count(), + SysrootWorkspace::Stitched(c) => c.crates().count(), + SysrootWorkspace::Empty => 0, } } - pub(crate) fn mode(&self) -> &SysrootMode { - &self.mode + pub(crate) fn workspace(&self) -> &SysrootWorkspace { + &self.workspace } } -// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated. impl Sysroot { /// Attempts to discover the toolchain's sysroot from the given `dir`. - pub fn discover( - dir: &AbsPath, - extra_env: &FxHashMap<String, String>, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { + pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Sysroot { let sysroot_dir = discover_sysroot_dir(dir, extra_env); let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| { discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env) }); - Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, sysroot_query_metadata) + Sysroot::assemble(Some(sysroot_dir), sysroot_src_dir) } pub fn discover_with_src_override( current_dir: &AbsPath, extra_env: &FxHashMap<String, String>, sysroot_src_dir: AbsPathBuf, - sysroot_query_metadata: SysrootQueryMetadata, ) -> Sysroot { let sysroot_dir = discover_sysroot_dir(current_dir, extra_env); - Sysroot::load_core_check( - Some(sysroot_dir), - Some(Ok(sysroot_src_dir)), - sysroot_query_metadata, - ) + Sysroot::assemble(Some(sysroot_dir), Some(Ok(sysroot_src_dir))) } - pub fn discover_sysroot_src_dir( - sysroot_dir: AbsPathBuf, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { + pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir) .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}")); - Sysroot::load_core_check( - Some(Ok(sysroot_dir)), - Some(sysroot_src_dir), - sysroot_query_metadata, - ) + Sysroot::assemble(Some(Ok(sysroot_dir)), Some(sysroot_src_dir)) } pub fn discover_rustc_src(&self) -> Option<ManifestPath> { get_rustc_src(self.root()?) } + pub fn new(sysroot_dir: Option<AbsPathBuf>, sysroot_src_dir: Option<AbsPathBuf>) -> Sysroot { + Self::assemble(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok)) + } + /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists. - pub fn tool(&self, tool: Tool) -> Command { + pub fn tool(&self, tool: Tool, current_dir: impl AsRef<Path>) -> Command { match self.root() { Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder @@ -176,15 +172,15 @@ impl Sysroot { if let Some(path) = probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) { - return Command::new(path); + return toolchain::command(path, current_dir); } } - let mut cmd = Command::new(tool.prefer_proxy()); + let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir); cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(root)); cmd } - _ => Command::new(tool.path()), + _ => toolchain::command(tool.path(), current_dir), } } @@ -202,90 +198,51 @@ impl Sysroot { }) } - pub fn load( - sysroot_dir: Option<AbsPathBuf>, - sysroot_src_dir: Option<AbsPathBuf>, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { - Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata) - } - - fn load_core_check( + fn assemble( sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>, sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>, - sysroot_query_metadata: SysrootQueryMetadata, ) -> Sysroot { - let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata); - if sysroot.error.is_none() { - if let Some(src_root) = &sysroot.src_root { - let has_core = match &sysroot.mode { - SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), - SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(), - SysrootMode::Empty => true, - }; - if !has_core { - let var_note = if env::var_os("RUST_SRC_PATH").is_some() { - " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" - } else { - ", try running `rustup component add rust-src` to possibly fix this" - }; - sysroot.error = Some(format!( - "sysroot at `{src_root}` is missing a `core` library{var_note}", - )); - } - } - } - sysroot - } - - fn load_( - sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>, - sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { - let sysroot_dir = match sysroot_dir { + let mut errors = String::new(); + let root = match sysroot_dir { Some(Ok(sysroot_dir)) => Some(sysroot_dir), Some(Err(e)) => { - return Sysroot { - root: None, - src_root: None, - mode: SysrootMode::Empty, - error: Some(e.to_string()), - } + format_to!(errors, "{e}\n"); + None } None => None, }; - let sysroot_src_dir = match sysroot_src_dir { - Some(Ok(sysroot_src_dir)) => sysroot_src_dir, + let src_root = match sysroot_src_dir { + Some(Ok(sysroot_src_dir)) => Some(sysroot_src_dir), Some(Err(e)) => { - return Sysroot { - root: sysroot_dir, - src_root: None, - mode: SysrootMode::Empty, - error: Some(e.to_string()), - } - } - None => { - return Sysroot { - root: sysroot_dir, - src_root: None, - mode: SysrootMode::Empty, - error: None, - } + format_to!(errors, "{e}\n"); + None } + None => None, }; - if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata { - let library_manifest = - ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap(); + Sysroot { + root, + src_root, + workspace: SysrootWorkspace::Empty, + error: errors.is_empty().not().then_some(errors), + } + } + + pub fn load_workspace(&mut self, sysroot_source_config: &SysrootSourceWorkspaceConfig) { + assert!(matches!(self.workspace, SysrootWorkspace::Empty), "workspace already loaded"); + let Self { root: _, src_root: Some(src_root), workspace, error: _ } = self else { return }; + if let SysrootSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config { + let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap(); if fs::metadata(&library_manifest).is_ok() { - if let Some(sysroot) = - Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir) + if let Some(loaded) = + Self::load_library_via_cargo(library_manifest, src_root, cargo_config) { - return sysroot; + *workspace = loaded; + self.load_core_check(); + return; } } } - tracing::debug!("Stitching sysroot library: {sysroot_src_dir}"); + tracing::debug!("Stitching sysroot library: {src_root}"); let mut stitched = Stitched { crates: Arena::default() }; @@ -293,7 +250,7 @@ impl Sysroot { let name = path.split('/').last().unwrap(); let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")] .into_iter() - .map(|it| sysroot_src_dir.join(it)) + .map(|it| src_root.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) .find(|it| fs::metadata(it).is_ok()); @@ -329,21 +286,39 @@ impl Sysroot { } } } - Sysroot { - root: sysroot_dir, - src_root: Some(sysroot_src_dir), - mode: SysrootMode::Stitched(stitched), - error: None, + *workspace = SysrootWorkspace::Stitched(stitched); + self.load_core_check(); + } + + fn load_core_check(&mut self) { + if self.error.is_none() { + if let Some(src_root) = &self.src_root { + let has_core = match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + SysrootWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), + SysrootWorkspace::Empty => true, + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" + } else { + ", try running `rustup component add rust-src` to possibly fix this" + }; + self.error = Some(format!( + "sysroot at `{src_root}` is missing a `core` library{var_note}", + )); + } + } } } fn load_library_via_cargo( library_manifest: ManifestPath, - sysroot_dir: &Option<AbsPathBuf>, sysroot_src_dir: &AbsPathBuf, - ) -> Option<Sysroot> { + cargo_config: &CargoMetadataConfig, + ) -> Option<SysrootWorkspace> { tracing::debug!("Loading library metadata: {library_manifest}"); - let mut cargo_config = CargoConfig::default(); + let mut cargo_config = cargo_config.clone(); // the sysroot uses `public-dependency`, so we make cargo think it's a nightly cargo_config.extra_env.insert( "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(), @@ -415,13 +390,8 @@ impl Sysroot { res.packages.remove(idx); }); - let cargo_workspace = CargoWorkspace::new(res, library_manifest); - Some(Sysroot { - root: sysroot_dir.clone(), - src_root: Some(sysroot_src_dir.clone()), - mode: SysrootMode::Workspace(cargo_workspace), - error: None, - }) + let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default()); + Some(SysrootWorkspace::Workspace(cargo_workspace)) } } @@ -429,11 +399,11 @@ fn discover_sysroot_dir( current_dir: &AbsPath, extra_env: &FxHashMap<String, String>, ) -> Result<AbsPathBuf> { - let mut rustc = Command::new(Tool::Rustc.path()); + let mut rustc = toolchain::command(Tool::Rustc.path(), current_dir); rustc.envs(extra_env); rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); - let stdout = utf8_stdout(rustc)?; + let stdout = utf8_stdout(&mut rustc)?; Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } @@ -461,11 +431,11 @@ fn discover_sysroot_src_dir_or_add_component( ) -> Result<AbsPathBuf> { discover_sysroot_src_dir(sysroot_path) .or_else(|| { - let mut rustup = Command::new(Tool::Rustup.prefer_proxy()); + let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir); rustup.envs(extra_env); - rustup.current_dir(current_dir).args(["component", "add", "rust-src"]); + rustup.args(["component", "add", "rust-src"]); tracing::info!("adding rust-src component by {:?}", rustup); - utf8_stdout(rustup).ok()?; + utf8_stdout(&mut rustup).ok()?; get_rust_src(sysroot_path) }) .ok_or_else(|| { diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs deleted file mode 100644 index 8a8a2d32558..00000000000 --- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Runs `rustc --print target-spec-json` to get the target_data_layout. - -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print target-spec-json` is discovered and invoked. -pub enum RustcDataLayoutConfig<'a> { - /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub fn get( - config: RustcDataLayoutConfig<'_>, - target: Option<&str>, - extra_env: &FxHashMap<String, String>, -) -> anyhow::Result<String> { - let process = |output: String| { - (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() - .ok_or_else(|| { - anyhow::format_err!("could not fetch target-spec-json from command output") - }) - }; - let sysroot = match config { - RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args([ - "rustc", - "-Z", - "unstable-options", - "--print", - "target-spec-json", - "--", - "-Z", - "unstable-options", - ]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - match utf8_stdout(cmd) { - Ok(output) => return process(output), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print target-spec-json`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcDataLayoutConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = Sysroot::tool(sysroot, Tool::Rustc); - cmd.envs(extra_env) - .args(["-Z", "unstable-options", "--print", "target-spec-json"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - process(utf8_stdout(cmd)?) -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index f3cf2d83eac..681bce3a5a6 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -12,39 +12,44 @@ use span::FileId; use triomphe::Arc; use crate::{ - sysroot::SysrootMode, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, - ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, SysrootQueryMetadata, - WorkspaceBuildScripts, + sysroot::SysrootWorkspace, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, + ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, + SysrootSourceWorkspaceConfig, WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { - load_cargo_with_overrides(file, CfgOverrides::default()) + let project_workspace = load_workspace_from_metadata(file); + to_crate_graph(project_workspace, &mut Default::default()) } fn load_cargo_with_overrides( file: &str, cfg_overrides: CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { + let project_workspace = + ProjectWorkspace { cfg_overrides, ..load_workspace_from_metadata(file) }; + to_crate_graph(project_workspace, &mut Default::default()) +} + +fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace { let meta: Metadata = get_test_json_file(file); let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace { + let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default()); + ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo: cargo_workspace, build_scripts: WorkspaceBuildScripts::default(), rustc: Err(None), - cargo_config_extra_env: Default::default(), error: None, set_test: true, }, - cfg_overrides, + cfg_overrides: Default::default(), sysroot: Sysroot::empty(), rustc_cfg: Vec::new(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - }; - to_crate_graph(project_workspace) + } } fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { @@ -59,7 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { target_layout: Err(Arc::from("test has no data layout")), cfg_overrides: Default::default(), }; - to_crate_graph(project_workspace) + to_crate_graph(project_workspace, &mut Default::default()) } fn get_test_json_file<T: DeserializeOwned>(file: &str) -> T { @@ -117,7 +122,9 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata) + let mut sysroot = Sysroot::new(Some(sysroot_dir), Some(sysroot_src_dir)); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); + sysroot } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { @@ -128,13 +135,15 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { ProjectJson::new(None, base, data) } -fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) { +fn to_crate_graph( + project_workspace: ProjectWorkspace, + file_map: &mut FxHashMap<AbsPathBuf, FileId>, +) -> (CrateGraph, ProcMacroPaths) { project_workspace.to_crate_graph( &mut { - let mut counter = 0; - move |_path| { - counter += 1; - Some(FileId::from_raw(counter)) + |path| { + let len = file_map.len() + 1; + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) } }, &Default::default(), @@ -223,24 +232,50 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { } #[test] +fn crate_graph_dedup_identical() { + let (mut crate_graph, proc_macros) = load_cargo("regex-metadata.json"); + + let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); + + crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros); + assert!(crate_graph.iter().eq(d_crate_graph.iter())); + assert_eq!(proc_macros, d_proc_macros); +} + +#[test] +fn crate_graph_dedup() { + let mut file_map = Default::default(); + + let ripgrep_workspace = load_workspace_from_metadata("ripgrep-metadata.json"); + let (mut crate_graph, _proc_macros) = to_crate_graph(ripgrep_workspace, &mut file_map); + assert_eq!(crate_graph.iter().count(), 71); + + let regex_workspace = load_workspace_from_metadata("regex-metadata.json"); + let (regex_crate_graph, mut regex_proc_macros) = to_crate_graph(regex_workspace, &mut file_map); + assert_eq!(regex_crate_graph.iter().count(), 50); + + crate_graph.extend(regex_crate_graph, &mut regex_proc_macros); + assert_eq!(crate_graph.iter().count(), 108); +} + +#[test] fn smoke_test_real_sysroot_cargo() { let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default(); let meta: Metadata = get_test_json_file("hello-world-metadata.json"); let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let sysroot = Sysroot::discover( + let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default()); + let mut sysroot = Sysroot::discover( AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), - SysrootQueryMetadata::CargoMetadata, ); - assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_))); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); + assert!(matches!(sysroot.workspace(), SysrootWorkspace::Workspace(_))); let project_workspace = ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo: cargo_workspace, build_scripts: WorkspaceBuildScripts::default(), rustc: Err(None), - cargo_config_extra_env: Default::default(), error: None, set_test: true, }, diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs new file mode 100644 index 00000000000..afcc8120794 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -0,0 +1,124 @@ +//! Get the built-in cfg flags for the to be compile platform. + +use anyhow::Context; +use cfg::CfgAtom; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout}; + +/// Uses `rustc --print cfg` to fetch the builtin cfgs. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap<String, String>, +) -> Vec<CfgAtom> { + let _p = tracing::info_span!("rustc_cfg::get").entered(); + + let rustc_cfgs = rustc_print_cfg(target, extra_env, config); + let rustc_cfgs = match rustc_cfgs { + Ok(cfgs) => cfgs, + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs"); + return vec![]; + } + }; + + // These are unstable but the standard libraries gate on them. + let unstable = vec![ + r#"target_has_atomic_equal_alignment="8""#, + r#"target_has_atomic_equal_alignment="16""#, + r#"target_has_atomic_equal_alignment="32""#, + r#"target_has_atomic_equal_alignment="64""#, + r#"target_has_atomic_equal_alignment="128""#, + r#"target_has_atomic_equal_alignment="ptr""#, + r#"target_has_atomic_load_store"#, + r#"target_has_atomic_load_store="8""#, + r#"target_has_atomic_load_store="16""#, + r#"target_has_atomic_load_store="32""#, + r#"target_has_atomic_load_store="64""#, + r#"target_has_atomic_load_store="128""#, + r#"target_has_atomic_load_store="ptr""#, + r#"target_thread_local"#, + r#"target_has_atomic"#, + ]; + let rustc_cfgs = + rustc_cfgs.lines().chain(unstable).map(crate::parse_cfg).collect::<Result<Vec<_>, _>>(); + match rustc_cfgs { + Ok(rustc_cfgs) => { + tracing::debug!(?rustc_cfgs, "rustc cfgs found"); + rustc_cfgs + } + Err(e) => { + tracing::error!(?e, "failed to parse rustc cfgs"); + vec![] + } + } +} + +fn rustc_print_cfg( + target: Option<&str>, + extra_env: &FxHashMap<String, String>, + config: QueryConfig<'_>, +) -> anyhow::Result<String> { + const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"]; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.args(["rustc"]).args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + cmd.args(["--", "-O"]); + + match utf8_stdout(&mut cmd) { + Ok(it) => return Ok(it), + Err(e) => { + tracing::warn!( + %e, + "failed to run `{cmd:?}`, falling back to invoking rustc directly" + ); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.args(RUSTC_ARGS); + cmd.arg("-O"); + if let Some(target) = target { + cmd.args(["--target", target]); + } + + utf8_stdout(&mut cmd).with_context(|| format!("unable to fetch cfgs via `{cmd:?}`")) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs new file mode 100644 index 00000000000..94645a91f65 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs @@ -0,0 +1,83 @@ +//! Runs `rustc --print target-spec-json` to get the target_data_layout. + +use anyhow::Context; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout, Sysroot}; + +/// Uses `rustc --print target-spec-json`. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap<String, String>, +) -> anyhow::Result<String> { + const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; + let process = |output: String| { + (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() + .ok_or_else(|| { + anyhow::format_err!("could not parse target-spec-json from command output") + }) + }; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([ + "--", + "-Z", + "unstable-options", + ]); + if let Some(target) = target { + cmd.args(["--target", target]); + } + match utf8_stdout(&mut cmd) { + Ok(output) => return process(output), + Err(e) => { + tracing::warn!(%e, "failed to run `{cmd:?}`, falling back to invoking rustc directly"); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = Sysroot::tool(sysroot, Tool::Rustc, current_dir); + cmd.envs(extra_env) + .env("RUSTC_BOOTSTRAP", "1") + .args(["-Z", "unstable-options"]) + .args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + utf8_stdout(&mut cmd) + .with_context(|| format!("unable to fetch target-data-layout via `{cmd:?}`")) + .and_then(process) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs new file mode 100644 index 00000000000..0476de58f23 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs @@ -0,0 +1,105 @@ +//! Functionality to discover the current build target(s). +use std::path::Path; + +use anyhow::Context; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout, ManifestPath, Sysroot}; + +/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s). +/// For rustc, runs `rustc --print -vV` to get the host target. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap<String, String>, +) -> anyhow::Result<Vec<String>> { + let _p = tracing::info_span!("target_tuple::get").entered(); + if let Some(target) = target { + return Ok(vec![target.to_owned()]); + } + + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + match cargo_config_build_target(cargo_toml, extra_env, sysroot) { + Some(it) => return Ok(it), + None => (sysroot, cargo_toml.parent().as_ref()), + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + rustc_discover_host_tuple(extra_env, sysroot, current_dir).map(|it| vec![it]) +} + +fn rustc_discover_host_tuple( + extra_env: &FxHashMap<String, String>, + sysroot: &Sysroot, + current_dir: &Path, +) -> anyhow::Result<String> { + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.arg("-vV"); + let stdout = utf8_stdout(&mut cmd) + .with_context(|| format!("unable to discover host platform via `{cmd:?}`"))?; + let field = "host: "; + let target = stdout.lines().find_map(|l| l.strip_prefix(field)); + if let Some(target) = target { + Ok(target.to_owned()) + } else { + // If we fail to resolve the host platform, it's not the end of the world. + Err(anyhow::format_err!("rustc -vV did not report host platform, got:\n{}", stdout)) + } +} + +fn cargo_config_build_target( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap<String, String>, + sysroot: &Sysroot, +) -> Option<Vec<String>> { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]); + // if successful we receive `build.target = "target-tuple"` + // or `build.target = ["<target 1>", ..]` + // this might be `error: config value `build.target` is not set` in which case we + // don't wanna log the error + utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok() +} + +// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"` +fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> { + let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); + + if !trimmed.starts_with('[') { + return Ok([trimmed.to_owned()].to_vec()); + } + + serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target") +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs new file mode 100644 index 00000000000..e795fdf1d64 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs @@ -0,0 +1,58 @@ +//! Get the version string of the toolchain. + +use anyhow::Context; +use rustc_hash::FxHashMap; +use semver::Version; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout}; + +pub(crate) fn get( + config: QueryConfig<'_>, + extra_env: &FxHashMap<String, String>, +) -> Result<Option<Version>, anyhow::Error> { + let (mut cmd, prefix) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + (sysroot.tool(Tool::Cargo, cargo_toml.parent()), "cargo ") + } + QueryConfig::Rustc(sysroot, current_dir) => { + (sysroot.tool(Tool::Rustc, current_dir), "rustc ") + } + }; + cmd.envs(extra_env); + cmd.arg("--version"); + let out = utf8_stdout(&mut cmd).with_context(|| format!("Failed to query rust toolchain version via `{cmd:?}`, is your toolchain setup correctly?"))?; + + let version = + out.strip_prefix(prefix).and_then(|it| Version::parse(it.split_whitespace().next()?).ok()); + if version.is_none() { + tracing::warn!("Failed to parse `{cmd:?}` output `{out}` as a semver version"); + } + anyhow::Ok(version) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 71ddee30910..a345c6bcce4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, iter, sync}; +use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync}; use anyhow::Context; use base_db::{ @@ -16,20 +16,19 @@ use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use semver::Version; use span::{Edition, FileId}; -use toolchain::Tool; use tracing::instrument; use triomphe::Arc; use crate::{ build_dependencies::BuildScriptOutput, - cargo_workspace::{DepKind, PackageData, RustLibSource}, + cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, - rustc_cfg::{self, RustcCfgConfig}, - sysroot::{SysrootCrate, SysrootMode}, - target_data_layout::{self, RustcDataLayoutConfig}, - utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, - Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, + sysroot::{SysrootCrate, SysrootWorkspace}, + toolchain_info::{rustc_cfg, target_data_layout, target_tuple, version, QueryConfig}, + CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, + ProjectJson, ProjectManifest, Sysroot, SysrootSourceWorkspaceConfig, TargetData, TargetKind, + WorkspaceBuildScripts, }; use tracing::{debug, error, info}; @@ -79,8 +78,6 @@ pub enum ProjectWorkspaceKind { /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been /// disabled or was otherwise not requested. rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>, - /// Environment variables set in the `.cargo/config` file. - cargo_config_extra_env: FxHashMap<String, String>, set_test: bool, }, /// Project workspace was specified using a `rust-project.json` file. @@ -100,8 +97,6 @@ pub enum ProjectWorkspaceKind { file: ManifestPath, /// Is this file a cargo script file? cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>, - /// Environment variables set in the `.cargo/config` file. - cargo_config_extra_env: FxHashMap<String, String>, set_test: bool, }, } @@ -111,14 +106,7 @@ impl fmt::Debug for ProjectWorkspace { // Make sure this isn't too verbose. let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self; match kind { - ProjectWorkspaceKind::Cargo { - cargo, - error: _, - build_scripts, - rustc, - cargo_config_extra_env, - set_test, - } => f + ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) .field("n_packages", &cargo.packages().len()) @@ -131,7 +119,6 @@ impl fmt::Debug for ProjectWorkspace { .field("n_cfg_overrides", &cfg_overrides.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) - .field("cargo_config_extra_env", &cargo_config_extra_env) .field("set_test", set_test) .field("build_scripts", &build_scripts.error().unwrap_or("ok")) .finish(), @@ -147,12 +134,7 @@ impl fmt::Debug for ProjectWorkspace { debug_struct.finish() } - ProjectWorkspaceKind::DetachedFile { - file, - cargo: cargo_script, - cargo_config_extra_env, - set_test, - } => f + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test } => f .debug_struct("DetachedFiles") .field("file", &file) .field("cargo_script", &cargo_script.is_some()) @@ -162,34 +144,12 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()) - .field("cargo_config_extra_env", &cargo_config_extra_env) .field("set_test", set_test) .finish(), } } } -fn get_toolchain_version( - current_dir: &AbsPath, - sysroot: &Sysroot, - tool: Tool, - extra_env: &FxHashMap<String, String>, - prefix: &str, -) -> Result<Option<Version>, anyhow::Error> { - let cargo_version = utf8_stdout({ - let mut cmd = Sysroot::tool(sysroot, tool); - cmd.envs(extra_env); - cmd.arg("--version").current_dir(current_dir); - cmd - }) - .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?; - anyhow::Ok( - cargo_version - .get(prefix.len()..) - .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()), - ) -} - impl ProjectWorkspace { pub fn load( manifest: ProjectManifest, @@ -220,167 +180,159 @@ impl ProjectWorkspace { ProjectWorkspace::load_detached_file(rust_file, config)? } ProjectManifest::CargoToml(cargo_toml) => { - let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustLibSource::Discover), None) => Sysroot::discover( - cargo_toml.parent(), - &config.extra_env, - config.sysroot_query_metadata, - ), - (Some(RustLibSource::Discover), Some(sysroot_src)) => { - Sysroot::discover_with_src_override( - cargo_toml.parent(), - &config.extra_env, - sysroot_src.clone(), - config.sysroot_query_metadata, - ) - } - (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir( - path.clone(), - config.sysroot_query_metadata, - ), - (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( - Some(sysroot.clone()), - Some(sysroot_src.clone()), - config.sysroot_query_metadata, - ), - (None, _) => Sysroot::empty(), - }; - tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); - - let rustc_dir = match &config.rustc_source { - Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) - .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), - Some(RustLibSource::Discover) => { - sysroot.discover_rustc_src().ok_or_else(|| { - Some("Failed to discover rustc source for sysroot.".to_owned()) - }) - } - None => Err(None), - }; + ProjectWorkspace::load_cargo(cargo_toml, config, progress)? + } + }; - let rustc = rustc_dir.and_then(|rustc_dir| { - info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); - match CargoWorkspace::fetch_metadata( - &rustc_dir, - cargo_toml.parent(), - &CargoConfig { - features: crate::CargoFeatures::default(), - ..config.clone() - }, - &sysroot, - false, - progress, - ) { - Ok((meta, _error)) => { - let workspace = CargoWorkspace::new(meta, cargo_toml.clone()); - let buildscripts = WorkspaceBuildScripts::rustc_crates( - &workspace, - cargo_toml.parent(), - &config.extra_env, - &sysroot - ); - Ok(Box::new((workspace, buildscripts))) - } - Err(e) => { - tracing::error!( - %e, - "Failed to read Cargo metadata from rustc source at {rustc_dir}", - ); - Err(Some(format!( - "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" - ))) - } - } - }); + Ok(res) + } - let toolchain = get_toolchain_version( + fn load_cargo( + cargo_toml: &ManifestPath, + config: &CargoConfig, + progress: &dyn Fn(String), + ) -> Result<ProjectWorkspace, anyhow::Error> { + let mut sysroot = match (&config.sysroot, &config.sysroot_src) { + (Some(RustLibSource::Discover), None) => { + Sysroot::discover(cargo_toml.parent(), &config.extra_env) + } + (Some(RustLibSource::Discover), Some(sysroot_src)) => { + Sysroot::discover_with_src_override( cargo_toml.parent(), - &sysroot, - Tool::Cargo, &config.extra_env, - "cargo ", - )?; - let rustc_cfg = rustc_cfg::get( - config.target.as_deref(), - &config.extra_env, - RustcCfgConfig::Cargo(&sysroot, cargo_toml), - ); + sysroot_src.clone(), + ) + } + (Some(RustLibSource::Path(path)), None) => { + Sysroot::discover_sysroot_src_dir(path.clone()) + } + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { + Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone())) + } + (None, _) => Sysroot::empty(), + }; - let cfg_overrides = config.cfg_overrides.clone(); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml), - config.target.as_deref(), - &config.extra_env, - ); - if let Err(e) = &data_layout { - tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); - } + let rustc_dir = match &config.rustc_source { + Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) + .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), + Some(RustLibSource::Discover) => sysroot + .discover_rustc_src() + .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())), + None => Err(None), + }; - let (meta, error) = CargoWorkspace::fetch_metadata( - cargo_toml, - cargo_toml.parent(), - config, - &sysroot, - false, - progress, + tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); + let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml); + let targets = + target_tuple::get(toolchain_config, config.target.as_deref(), &config.extra_env) + .unwrap_or_default(); + let toolchain = version::get(toolchain_config, &config.extra_env) + .inspect_err(|e| { + tracing::error!(%e, + "failed fetching toolchain version for {cargo_toml:?} workspace" ) - .with_context(|| { - format!( - "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", - ) - })?; - let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); - - let cargo_config_extra_env = - cargo_config_env(cargo_toml, &config.extra_env, &sysroot); - ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo, - build_scripts: WorkspaceBuildScripts::default(), - rustc, - cargo_config_extra_env, - error: error.map(Arc::new), - set_test: config.set_test, - }, - sysroot, - rustc_cfg, - cfg_overrides, - toolchain, - target_layout: data_layout - .map(Arc::from) - .map_err(|it| Arc::from(it.to_string())), + }) + .ok() + .flatten(); + let rustc_cfg = + rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), &config.extra_env); + let cfg_overrides = config.cfg_overrides.clone(); + let data_layout = target_data_layout::get( + toolchain_config, + targets.first().map(Deref::deref), + &config.extra_env, + ); + if let Err(e) = &data_layout { + tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); + } + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata( + sysroot_metadata_config(&config.extra_env, &targets), + )); + + let rustc = rustc_dir.and_then(|rustc_dir| { + info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); + match CargoWorkspace::fetch_metadata( + &rustc_dir, + cargo_toml.parent(), + &CargoMetadataConfig { + features: crate::CargoFeatures::default(), + targets: targets.clone(), + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) { + Ok((meta, _error)) => { + let workspace = CargoWorkspace::new(meta, cargo_toml.clone(), Env::default()); + let build_scripts = WorkspaceBuildScripts::rustc_crates( + &workspace, + cargo_toml.parent(), + &config.extra_env, + &sysroot, + ); + Ok(Box::new((workspace, build_scripts))) + } + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {rustc_dir}", + ); + Err(Some(format!( + "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" + ))) } } - }; + }); + + let (meta, error) = CargoWorkspace::fetch_metadata( + cargo_toml, + cargo_toml.parent(), + &CargoMetadataConfig { + features: config.features.clone(), + targets, + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) + .with_context(|| { + format!( + "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", + ) + })?; + let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot); + let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env); - Ok(res) + Ok(ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo, + build_scripts: WorkspaceBuildScripts::default(), + rustc, + error: error.map(Arc::new), + set_test: config.set_test, + }, + sysroot, + rustc_cfg, + cfg_overrides, + toolchain, + target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + }) } pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace { - let sysroot = Sysroot::load( - project_json.sysroot.clone(), - project_json.sysroot_src.clone(), - config.sysroot_query_metadata, - ); - let cfg_config = RustcCfgConfig::Rustc(&sysroot); - let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot); - let toolchain = match get_toolchain_version( - project_json.path(), - &sysroot, - Tool::Rustc, - &config.extra_env, - "rustc ", - ) { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None - } - }; + let mut sysroot = + Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone()); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::Stitched); + let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref()); + let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); let target = config.target.as_deref(); - let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config); - let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env); + let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env); + let data_layout = target_data_layout::get(query_config, target, &config.extra_env); ProjectWorkspace { kind: ProjectWorkspaceKind::Json(project_json), sysroot, @@ -396,49 +348,50 @@ impl ProjectWorkspace { config: &CargoConfig, ) -> anyhow::Result<ProjectWorkspace> { let dir = detached_file.parent(); - let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => { - Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata) - } - Some(RustLibSource::Discover) => { - Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) - } + let mut sysroot = match &config.sysroot { + Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()), + Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env), None => Sysroot::empty(), }; - let toolchain = - match get_toolchain_version(dir, &sysroot, Tool::Rustc, &config.extra_env, "rustc ") { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None - } - }; - - let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot)); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), - None, - &config.extra_env, - ); - - let cargo_script = - CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ()) - .ok() - .map(|(ws, error)| { - ( - CargoWorkspace::new(ws, detached_file.clone()), - WorkspaceBuildScripts::default(), - error.map(Arc::new), - ) - }); + let query_config = QueryConfig::Cargo(&sysroot, detached_file); + let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); + let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) + .unwrap_or_default(); + let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); + let data_layout = target_data_layout::get(query_config, None, &config.extra_env); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata( + sysroot_metadata_config(&config.extra_env, &targets), + )); + + let cargo_script = CargoWorkspace::fetch_metadata( + detached_file, + dir, + &CargoMetadataConfig { + features: config.features.clone(), + targets, + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + &|_| (), + ) + .ok() + .map(|(ws, error)| { + let cargo_config_extra_env = + cargo_config_env(detached_file, &config.extra_env, &sysroot); + ( + CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env), + WorkspaceBuildScripts::default(), + error.map(Arc::new), + ) + }); - let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot); Ok(ProjectWorkspace { kind: ProjectWorkspaceKind::DetachedFile { file: detached_file.to_owned(), cargo: cargo_script, - cargo_config_extra_env, set_test: config.set_test, }, sysroot, @@ -565,8 +518,8 @@ impl ProjectWorkspace { /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec<PackageRoot> { let mk_sysroot = || { - let mut r = match self.sysroot.mode() { - SysrootMode::Workspace(ws) => ws + let mut r = match self.sysroot.workspace() { + SysrootWorkspace::Workspace(ws) => ws .packages() .filter_map(|pkg| { if ws[pkg].is_local { @@ -587,7 +540,7 @@ impl ProjectWorkspace { Some(PackageRoot { is_local: false, include, exclude }) }) .collect(), - SysrootMode::Stitched(_) | SysrootMode::Empty => vec![], + SysrootWorkspace::Stitched(_) | SysrootWorkspace::Empty => vec![], }; r.push(PackageRoot { @@ -601,32 +554,25 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Json(project) => project .crates() .map(|(_, krate)| { - let build_files = project - .crates() - .filter_map(|(_, krate)| { - krate.build.as_ref().map(|build| build.build_file.clone()) - }) - // FIXME: PackageRoots dont allow specifying files, only directories - .filter_map(|build_file| { - self.workspace_root().join(build_file).parent().map(ToOwned::to_owned) - }); + // FIXME: PackageRoots dont allow specifying files, only directories + let build_file = krate + .build + .as_ref() + .map(|build| self.workspace_root().join(&build.build_file)) + .as_deref() + .and_then(AbsPath::parent) + .map(ToOwned::to_owned); + PackageRoot { is_local: krate.is_workspace_member, - include: krate.include.iter().cloned().chain(build_files).collect(), + include: krate.include.iter().cloned().chain(build_file).collect(), exclude: krate.exclude.clone(), } }) .chain(mk_sysroot()) .unique() .collect(), - ProjectWorkspaceKind::Cargo { - cargo, - rustc, - build_scripts, - cargo_config_extra_env: _, - error: _, - set_test: _, - } => { + ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => { cargo .packages() .map(|pkg| { @@ -763,23 +709,18 @@ impl ProjectWorkspace { extra_env, cfg_overrides, ), - ProjectWorkspaceKind::Cargo { - cargo, - rustc, - build_scripts, - cargo_config_extra_env: _, - error: _, - set_test, - } => cargo_to_crate_graph( - load, - rustc.as_ref().map(|a| a.as_ref()).ok(), - cargo, - sysroot, - rustc_cfg.clone(), - cfg_overrides, - build_scripts, - *set_test, - ), + ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test } => { + cargo_to_crate_graph( + load, + rustc.as_ref().map(|a| a.as_ref()).ok(), + cargo, + sysroot, + rustc_cfg.clone(), + cfg_overrides, + build_scripts, + *set_test, + ) + } ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => { if let Some((cargo, build_scripts, _)) = cargo_script { cargo_to_crate_graph( @@ -824,7 +765,6 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo, rustc, - cargo_config_extra_env, build_scripts: _, error: _, set_test: _, @@ -832,16 +772,11 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo: o_cargo, rustc: o_rustc, - cargo_config_extra_env: o_cargo_config_extra_env, build_scripts: _, error: _, set_test: _, }, - ) => { - cargo == o_cargo - && rustc == o_rustc - && cargo_config_extra_env == o_cargo_config_extra_env - } + ) => cargo == o_cargo && rustc == o_rustc, (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => { project == o_project } @@ -849,20 +784,14 @@ impl ProjectWorkspace { ProjectWorkspaceKind::DetachedFile { file, cargo: Some((cargo_script, _, _)), - cargo_config_extra_env, set_test: _, }, ProjectWorkspaceKind::DetachedFile { file: o_file, cargo: Some((o_cargo_script, _, _)), - cargo_config_extra_env: o_cargo_config_extra_env, set_test: _, }, - ) => { - file == o_file - && cargo_script == o_cargo_script - && cargo_config_extra_env == o_cargo_config_extra_env - } + ) => file == o_file && cargo_script == o_cargo_script, _ => return false, }) && sysroot == o_sysroot && rustc_cfg == o_rustc_cfg @@ -921,7 +850,11 @@ fn project_json_to_crate_graph( let target_cfgs = match target.as_deref() { Some(target) => cfg_cache.entry(target).or_insert_with(|| { - rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot)) + rustc_cfg::get( + QueryConfig::Rustc(sysroot, project.project_root().as_ref()), + Some(target), + extra_env, + ) }), None => &rustc_cfg, }; @@ -1374,15 +1307,13 @@ fn add_target_crate_root( opts }; - let mut env = Env::default(); + let mut env = cargo.env().clone(); inject_cargo_package_env(&mut env, pkg); inject_cargo_env(&mut env); - inject_rustc_tool_env(&mut env, cargo, cargo_name, kind); + inject_rustc_tool_env(&mut env, cargo_name, kind); if let Some(envs) = build_data.map(|(it, _)| &it.envs) { - for (k, v) in envs { - env.set(k, v.clone()); - } + env.extend_from_other(envs); } let crate_id = crate_graph.add_crate_root( file_id, @@ -1433,8 +1364,8 @@ fn sysroot_to_crate_graph( load: FileLoader<'_>, ) -> (SysrootPublicDeps, Option<CrateId>) { let _p = tracing::info_span!("sysroot_to_crate_graph").entered(); - match sysroot.mode() { - SysrootMode::Workspace(cargo) => { + match sysroot.workspace() { + SysrootWorkspace::Workspace(cargo) => { let (mut cg, mut pm) = cargo_to_crate_graph( load, None, @@ -1509,7 +1440,7 @@ fn sysroot_to_crate_graph( (SysrootPublicDeps { deps: pub_deps }, libproc_macro) } - SysrootMode::Stitched(stitched) => { + SysrootWorkspace::Stitched(stitched) => { let cfg_options = Arc::new({ let mut cfg_options = CfgOptions::default(); cfg_options.extend(rustc_cfg); @@ -1562,7 +1493,7 @@ fn sysroot_to_crate_graph( stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); (public_deps, libproc_macro) } - SysrootMode::Empty => (SysrootPublicDeps { deps: vec![] }, None), + SysrootWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None), } } @@ -1597,3 +1528,15 @@ fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { tracing::warn!("{}", err) } } + +fn sysroot_metadata_config( + extra_env: &FxHashMap<String, String>, + targets: &[String], +) -> CargoMetadataConfig { + CargoMetadataConfig { + features: Default::default(), + targets: targets.to_vec(), + extra_args: Default::default(), + extra_env: extra_env.clone(), + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt index 90f41a9c2fc..2026ab2b8c2 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt @@ -479,7 +479,7 @@ }, 11: CrateData { root_file_id: FileId( - 12, + 11, ), edition: Edition2018, version: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json new file mode 100644 index 00000000000..371464dd20a --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json @@ -0,0 +1,6420 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "0.1.10", + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "docopt", + "version": "1.1.1", + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Command line argument parsing.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std", + "unicode" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "docopt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "docopt-wordlist", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cargo", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cp", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "decode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "hashmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "optional_command", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "verbose_multiple", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "docopt", + "argument", + "command", + "argv" + ], + "readme": "README.md", + "repository": "https://github.com/docopt/docopt.rs", + "homepage": "https://github.com/docopt/docopt.rs", + "documentation": "http://burntsushi.net/rustdoc/docopt/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "getrandom", + "version": "0.2.9", + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A small cross-platform library for retrieving random data from system source", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "js-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.62", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen-test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.18", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(target_os = \"wasi\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.139", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "getrandom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "custom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "normal", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "rdrand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "custom": [], + "js": [ + "wasm-bindgen", + "js-sys" + ], + "js-sys": [ + "dep:js-sys" + ], + "rdrand": [], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "libc/rustc-dep-of-std", + "wasi/rustc-dep-of-std" + ], + "std": [], + "test-in-browser": [], + "wasm-bindgen": [ + "dep:wasm-bindgen" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "custom" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers" + ], + "categories": [ + "os", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-random/getrandom", + "homepage": null, + "documentation": "https://docs.rs/getrandom", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel <loebel.marvin@gmail.com>" + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap", + "version": "0.6.2", + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert <dan@danburkert.com>" + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/danburkert/memmap-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>", + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quickcheck", + "version": "1.0.3", + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automatic property based testing with shrinking.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "env_logger", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quickcheck", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "btree_set_range", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "out_of_bounds", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse_single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sieve", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sort", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "regex", + "use_logging" + ], + "env_logger": [ + "dep:env_logger" + ], + "log": [ + "dep:log" + ], + "regex": [ + "env_logger/regex" + ], + "use_logging": [ + "log", + "env_logger" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "development-tools::testing" + ], + "keywords": [ + "testing", + "quickcheck", + "property", + "shrinking", + "fuzz" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/quickcheck", + "homepage": "https://github.com/BurntSushi/quickcheck", + "documentation": "https://docs.rs/quickcheck", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "rand", + "version": "0.8.5", + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Random number generators and other randomness functionality.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.7", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [ + "into_bits" + ], + "target": null, + "registry": null + }, + { + "name": "rand_chacha", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.103", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_pcg", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.22", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [ + "rand_core/alloc" + ], + "default": [ + "std", + "std_rng" + ], + "getrandom": [ + "rand_core/getrandom" + ], + "libc": [ + "dep:libc" + ], + "log": [ + "dep:log" + ], + "min_const_gen": [], + "nightly": [], + "packed_simd": [ + "dep:packed_simd" + ], + "rand_chacha": [ + "dep:rand_chacha" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde", + "rand_core/serde1" + ], + "simd_support": [ + "packed_simd" + ], + "small_rng": [], + "std": [ + "rand_core/std", + "rand_chacha/std", + "alloc", + "getrandom", + "libc" + ], + "std_rng": [ + "rand_chacha" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "features": [ + "small_rng", + "serde1" + ] + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "rand_core", + "version": "0.6.4", + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Core random number generator traits and tools for implementation.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "getrandom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand_core", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "getrandom": [ + "dep:getrandom" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "std": [ + "alloc", + "getrandom", + "getrandom/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "all-features": true + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand_core", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.7.1", + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6.27", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$regex/tests/test_default.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$regex/tests/test_default_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$regex/tests/test_nfa.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$regex/tests/test_backtrack.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$regex/tests/test_crates_regex.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-benchmark", + "version": "0.1.0", + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Regex benchmarks for Rust's and other engines.", + "source": null, + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libpcre-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "onig", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.9", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-run-one", + "src_path": "$ROOT$regex/bench/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/bench/src/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$regex/bench/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "libpcre-sys": [ + "dep:libpcre-sys" + ], + "onig": [ + "dep:onig" + ], + "re-onig": [ + "onig" + ], + "re-pcre1": [ + "libpcre-sys" + ], + "re-pcre2": [], + "re-re2": [], + "re-rust": [], + "re-rust-bytes": [], + "re-tcl": [] + }, + "manifest_path": "$ROOT$regex/bench/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-debug", + "version": "0.1.0", + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A tool useful for debugging regular expressions.", + "source": null, + "dependencies": [ + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-debug", + "src_path": "$ROOT$regex/regex-debug/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.28", + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$regex/regex-syntax/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "rure", + "version": "0.2.2", + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A C API for Rust's regular expression library.\n", + "source": null, + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + } + ], + "targets": [ + { + "kind": [ + "staticlib", + "cdylib", + "rlib" + ], + "crate_types": [ + "staticlib", + "cdylib", + "rlib" + ], + "name": "rure", + "src_path": "$ROOT$regex/regex-capi/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar <erick.tryzelaar@gmail.com>", + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar <erick.tryzelaar@gmail.com>", + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "strsim", + "version": "0.10.0", + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo <danny@dannyguo.com>" + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "wasi", + "version": "0.11.0+wasi-snapshot-preview1", + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "license_file": null, + "description": "Experimental WASI API bindings for Rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-alloc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "wasi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "rustc-std-workspace-alloc" + ], + "rustc-std-workspace-alloc": [ + "dep:rustc-std-workspace-alloc" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Cranelift Project Developers" + ], + "categories": [ + "no-std", + "wasm" + ], + "keywords": [ + "webassembly", + "wasm" + ], + "readme": "README.md", + "repository": "https://github.com/bytecodealliance/wasi", + "homepage": null, + "documentation": "https://docs.rs/wasi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "wasi", + "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"wasi\")" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand_core", + "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom", + "small_rng" + ] + }, + { + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "getrandom", + "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom" + ] + }, + { + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quickcheck", + "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "cfg_if", + "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "dependencies": [ + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "regex 1.7.1 (path+file:///$ROOT$regex)" + }, + "target_directory": "$ROOT$regex/target", + "version": 1, + "workspace_root": "$ROOT$regex", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json new file mode 100644 index 00000000000..131ff5dd715 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json @@ -0,0 +1,12816 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "aho-corasick", + "version": "1.0.1", + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.17", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std", + "perf-literal" + ], + "logging": [ + "dep:log" + ], + "perf-literal": [ + "dep:memchr" + ], + "std": [ + "memchr?/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "pattern", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "atty", + "version": "0.2.14", + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple interface for querying atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hermit-abi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(target_os = \"hermit\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "consoleapi", + "processenv", + "minwinbase", + "minwindef", + "winbase" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "softprops <d.tangren@gmail.com>" + ], + "categories": [], + "keywords": [ + "terminal", + "tty", + "isatty" + ], + "readme": "README.md", + "repository": "https://github.com/softprops/atty", + "homepage": "https://github.com/softprops/atty", + "documentation": "http://softprops.github.io/atty", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "base64", + "version": "0.20.0", + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "encodes and decodes base64 as bytes or utf8", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "rstest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rstest_reuse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "structopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.26", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "encode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tests", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benchmarks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alice Maz <alice@alicemaz.com>", + "Marshall Pierce <marshall@mpierce.org>" + ], + "categories": [ + "encoding" + ], + "keywords": [ + "base64", + "utf8", + "encode", + "decode", + "no_std" + ], + "readme": "README.md", + "repository": "https://github.com/marshallpierce/rust-base64", + "homepage": null, + "documentation": "https://docs.rs/base64", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.57.0" + }, + { + "name": "bitflags", + "version": "1.3.2", + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to generate structures which behave like bitflags.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bitflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "basic", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compile", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "example_generated": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "example_generated" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "no-std" + ], + "keywords": [ + "bit", + "bitmask", + "bitflags", + "flags" + ], + "readme": "README.md", + "repository": "https://github.com/bitflags/bitflags", + "homepage": "https://github.com/bitflags/bitflags", + "documentation": "https://docs.rs/bitflags", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "bstr", + "version": "1.4.0", + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A string type that is not required to be valid UTF-8.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.14.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-automata", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.85", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-parse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-segmentation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bstr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde?/alloc" + ], + "default": [ + "std", + "unicode" + ], + "serde": [ + "dep:serde" + ], + "std": [ + "alloc", + "memchr/std", + "serde?/std" + ], + "unicode": [ + "dep:once_cell", + "dep:regex-automata" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing", + "encoding" + ], + "keywords": [ + "string", + "str", + "byte", + "bytes", + "text" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/bstr", + "homepage": "https://github.com/BurntSushi/bstr", + "documentation": "https://docs.rs/bstr", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60" + }, + { + "name": "bytecount", + "version": "0.6.3", + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0/MIT", + "license_file": null, + "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.8", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bytecount", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "check", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "generic-simd": [ + "packed_simd" + ], + "html_report": [], + "packed_simd": [ + "dep:packed_simd" + ], + "runtime-dispatch-simd": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andre Bogus <bogusandre@gmail.de>", + "Joshua Landau <joshua@landau.ws>" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/llogiq/bytecount", + "homepage": null, + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "clap", + "version": "2.34.0", + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bitflags", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clippy", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "~0.0.166", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "textwrap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "vec_map", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "yaml-rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ansi_term", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(not(windows))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "clap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "ansi_term": [ + "dep:ansi_term" + ], + "atty": [ + "dep:atty" + ], + "clippy": [ + "dep:clippy" + ], + "color": [ + "ansi_term", + "atty" + ], + "debug": [], + "default": [ + "suggestions", + "color", + "vec_map" + ], + "doc": [ + "yaml" + ], + "nightly": [], + "no_cargo": [], + "strsim": [ + "dep:strsim" + ], + "suggestions": [ + "strsim" + ], + "term_size": [ + "dep:term_size" + ], + "unstable": [], + "vec_map": [ + "dep:vec_map" + ], + "wrap_help": [ + "term_size", + "textwrap/term_size" + ], + "yaml": [ + "yaml-rust" + ], + "yaml-rust": [ + "dep:yaml-rust" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "doc" + ] + } + } + }, + "publish": null, + "authors": [ + "Kevin K. <kbknapp@gmail.com>" + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "argument", + "cli", + "arg", + "parser", + "parse" + ], + "readme": "README.md", + "repository": "https://github.com/clap-rs/clap", + "homepage": "https://clap.rs/", + "documentation": "https://docs.rs/clap/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "crossbeam-channel", + "version": "0.5.8", + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Multi-producer multi-consumer channels for message passing", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.13.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "signal-hook", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "fibonacci", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "matching", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "stopwatch", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "after", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "array", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "golang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "iter", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "list", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "mpsc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "never", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ready", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "same_channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select_macro", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread_locals", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zero", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "crossbeam", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "crossbeam-utils": [ + "dep:crossbeam-utils" + ], + "default": [ + "std" + ], + "std": [ + "crossbeam-utils/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures" + ], + "keywords": [ + "channel", + "mpmc", + "select", + "golang", + "message" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "crossbeam-utils", + "version": "0.8.15", + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Utilities for concurrent programming", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "loom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(crossbeam_loom)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-utils", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cache_padded", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "parker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "sharded_lock", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "wait_group", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std" + ], + "loom": [ + "dep:loom" + ], + "nightly": [], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures", + "no-std" + ], + "keywords": [ + "scoped", + "thread", + "atomic", + "cache" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "encoding_rs", + "version": "0.8.32", + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "license_file": null, + "description": "A Gecko-oriented implementation of the Encoding Standard", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.4", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "default": [ + "alloc" + ], + "fast-big5-hanzi-encode": [], + "fast-gb-hanzi-encode": [], + "fast-hangul-encode": [], + "fast-hanja-encode": [], + "fast-kanji-encode": [], + "fast-legacy-encode": [ + "fast-hangul-encode", + "fast-hanja-encode", + "fast-kanji-encode", + "fast-gb-hanzi-encode", + "fast-big5-hanzi-encode" + ], + "less-slow-big5-hanzi-encode": [], + "less-slow-gb-hanzi-encode": [], + "less-slow-kanji-encode": [], + "packed_simd": [ + "dep:packed_simd" + ], + "serde": [ + "dep:serde" + ], + "simd-accel": [ + "packed_simd", + "packed_simd/into_bits" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Henri Sivonen <hsivonen@hsivonen.fi>" + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "internationalization" + ], + "keywords": [ + "encoding", + "web", + "unicode", + "charset" + ], + "readme": "README.md", + "repository": "https://github.com/hsivonen/encoding_rs", + "homepage": "https://docs.rs/encoding_rs/", + "documentation": "https://docs.rs/encoding_rs/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "encoding_rs_io", + "version": "0.1.7", + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Streaming transcoding for encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs_io", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "email" + ], + "keywords": [ + "encoding", + "transcoding", + "stream", + "io", + "read" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/encoding_rs_io", + "homepage": null, + "documentation": "https://docs.rs/encoding_rs_io", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "fnv", + "version": "1.0.7", + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 / MIT", + "license_file": null, + "description": "Fowler–Noll–Vo hash function", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "fnv", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/servo/rust-fnv", + "homepage": null, + "documentation": "https://doc.servo.org/fnv/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "glob", + "version": "0.3.1", + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Support for matching file paths against Unix shell style patterns.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "glob", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "glob-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "filesystem" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/glob", + "homepage": "https://github.com/rust-lang/glob", + "documentation": "https://docs.rs/glob/0.3.1", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "globset", + "version": "0.4.10", + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "fnv", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "perf", + "std" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.104", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "glob", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.45", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "globset", + "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "log" + ], + "log": [ + "dep:log" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "simd-accel": [] + }, + "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "glob", + "multiple", + "set", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "documentation": "https://docs.rs/globset", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep", + "version": "0.2.11", + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "grep-cli", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/cli" + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-pcre2", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/pcre2" + }, + { + "name": "grep-printer", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/printer" + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep", + "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "simplegrep", + "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "grep-pcre2": [ + "dep:grep-pcre2" + ], + "pcre2": [ + "grep-pcre2" + ], + "simd-accel": [ + "grep-searcher/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "documentation": "https://docs.rs/grep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-cli", + "version": "0.1.7", + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Utilities for search oriented command line applications.\n", + "source": null, + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-cli", + "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "cli", + "utility", + "util" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "documentation": "https://docs.rs/grep-cli", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-matcher", + "version": "0.1.6", + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A trait for regular expressions, with a focus on line oriented search.\n", + "source": null, + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-matcher", + "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "pattern", + "trait" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "documentation": "https://docs.rs/grep-matcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-pcre2", + "version": "0.1.6", + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use PCRE2 with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "pcre2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-pcre2", + "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "pcre", + "backreference", + "look" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "documentation": "https://docs.rs/grep-pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-printer", + "version": "0.1.7", + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n", + "source": null, + "dependencies": [ + { + "name": "base64", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.20.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.27", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-printer", + "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "base64": [ + "dep:base64" + ], + "default": [ + "serde1" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "base64", + "serde", + "serde_json" + ], + "serde_json": [ + "dep:serde_json" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "grep", + "pattern", + "print", + "printer", + "sink" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "documentation": "https://docs.rs/grep-printer", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-regex", + "version": "0.1.11", + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use Rust's regex library with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-regex", + "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "search", + "pattern", + "line" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "documentation": "https://docs.rs/grep-regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-searcher", + "version": "0.1.11", + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "bytecount", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.14", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs_io", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.3", + "kind": null, + "rename": "memmap", + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-searcher", + "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "search-stdin", + "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "default": [ + "bytecount/runtime-dispatch-simd" + ], + "simd-accel": [ + "encoding_rs/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "documentation": "https://docs.rs/grep-searcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "hermit-abi", + "version": "0.1.19", + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.51", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "hermit-abi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "docs": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins/rustc-dep-of-std", + "libc/rustc-dep-of-std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-unknown-hermit", + "features": [ + "docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Stefan Lankes" + ], + "categories": [ + "os" + ], + "keywords": [ + "unikernel", + "libos" + ], + "readme": "README.md", + "repository": "https://github.com/hermitcore/libhermit-rs", + "homepage": null, + "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "ignore", + "version": "0.4.20", + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n", + "source": null, + "dependencies": [ + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-channel", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ignore", + "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "walk", + "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "gitignore_matched_path_or_any_parents_tests", + "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "simd-accel": [ + "globset/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "glob", + "ignore", + "gitignore", + "pattern", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "documentation": "https://docs.rs/ignore", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "itoa", + "version": "1.0.6", + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Fast integer primitive to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "itoa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "integer" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/itoa", + "homepage": null, + "documentation": "https://docs.rs/itoa", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "jemalloc-sys", + "version": "0.5.3+5.3.0-patched", + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Rust FFI bindings to jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemalloc-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_empty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_set", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "unprefixed_malloc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "background_threads": [ + "background_threads_runtime_support" + ], + "background_threads_runtime_support": [], + "debug": [], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [], + "profiling": [], + "stats": [], + "unprefixed_malloc_on_supported_platforms": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>", + "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>", + "The TiKV Project Developers" + ], + "categories": [], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator-sys", + "edition": "2018", + "links": "jemalloc", + "default_run": null, + "rust_version": null + }, + { + "name": "jemallocator", + "version": "0.5.0", + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A Rust allocator backed by jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jemalloc-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "paste", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemallocator", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_defaults", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_enabled", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "grow_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloctl", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "shrink_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke_ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "usable_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "roundtrip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc_trait": [], + "background_threads": [ + "jemalloc-sys/background_threads" + ], + "background_threads_runtime_support": [ + "jemalloc-sys/background_threads_runtime_support" + ], + "debug": [ + "jemalloc-sys/debug" + ], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [ + "jemalloc-sys/disable_initial_exec_tls" + ], + "profiling": [ + "jemalloc-sys/profiling" + ], + "stats": [ + "jemalloc-sys/stats" + ], + "unprefixed_malloc_on_supported_platforms": [ + "jemalloc-sys/unprefixed_malloc_on_supported_platforms" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [], + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>", + "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>", + "Simon Sapin <simon.sapin@exyr.org>", + "Steven Fackler <sfackler@gmail.com>", + "The TiKV Project Developers" + ], + "categories": [ + "memory-management", + "api-bindings" + ], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "jobserver", + "version": "0.1.26", + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "An implementation of the GNU make jobserver for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "futures", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-process", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.50", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jobserver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "server", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client-of-myself", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "make-as-a-client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "helper", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/jobserver-rs", + "homepage": "https://github.com/alexcrichton/jobserver-rs", + "documentation": "https://docs.rs/jobserver", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel <loebel.marvin@gmail.com>" + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "log", + "version": "0.4.17", + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A lightweight logging facade for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "test" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "log", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "filters", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "macros", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "value", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "kv_unstable": [ + "value-bag" + ], + "kv_unstable_serde": [ + "kv_unstable_std", + "value-bag/serde", + "serde" + ], + "kv_unstable_std": [ + "std", + "kv_unstable", + "value-bag/error" + ], + "kv_unstable_sval": [ + "kv_unstable", + "value-bag/sval", + "sval" + ], + "max_level_debug": [], + "max_level_error": [], + "max_level_info": [], + "max_level_off": [], + "max_level_trace": [], + "max_level_warn": [], + "release_max_level_debug": [], + "release_max_level_error": [], + "release_max_level_info": [], + "release_max_level_off": [], + "release_max_level_trace": [], + "release_max_level_warn": [], + "serde": [ + "dep:serde" + ], + "std": [], + "sval": [ + "dep:sval" + ], + "value-bag": [ + "dep:value-bag" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "serde", + "kv_unstable_std", + "kv_unstable_sval", + "kv_unstable_serde" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "development-tools::debugging" + ], + "keywords": [ + "logging" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/log", + "homepage": null, + "documentation": "https://docs.rs/log", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap2", + "version": "0.5.10", + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "stable_deref_trait", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "owning_ref", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "stable_deref_trait": [ + "dep:stable_deref_trait" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert <dan@danburkert.com>", + "Yevhenii Reizner <razrfalcon@gmail.com>" + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/RazrFalcon/memmap2-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "once_cell", + "version": "1.17.1", + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Single assignment cells and lazy values.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atomic-polyfill", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "atomic_polyfill", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "critical_section", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "parking_lot_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.9.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.1", + "kind": "dev", + "rename": "critical_section", + "optional": false, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "once_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_acquire", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_vs_lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reentrant_init_deadlocks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "test_synchronization", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "it", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "alloc": [ + "race" + ], + "atomic-polyfill": [ + "critical-section" + ], + "atomic_polyfill": [ + "dep:atomic_polyfill" + ], + "critical-section": [ + "critical_section", + "atomic_polyfill" + ], + "critical_section": [ + "dep:critical_section" + ], + "default": [ + "std" + ], + "parking_lot": [ + "parking_lot_core" + ], + "parking_lot_core": [ + "dep:parking_lot_core" + ], + "race": [], + "std": [ + "alloc" + ], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Aleksey Kladov <aleksey.kladov@gmail.com>" + ], + "categories": [ + "rust-patterns", + "memory-management" + ], + "keywords": [ + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/matklad/once_cell", + "homepage": null, + "documentation": "https://docs.rs/once_cell", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "pcre2", + "version": "0.2.3", + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "High level wrapper library for PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.46", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pcre2-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit", + "perl" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pcre2-sys", + "version": "0.2.5", + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Low level bindings to PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "parallel" + ], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "external-ffi-bindings" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2-sys", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>", + "Alex Crichton <alex@alexcrichton.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-automata", + "version": "0.1.10", + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automata construction and matching using regular expressions.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "toml", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-automata", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "fst": [ + "dep:fst" + ], + "regex-syntax": [ + "dep:regex-syntax" + ], + "std": [ + "regex-syntax" + ], + "transducer": [ + "std", + "fst" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "regex", + "dfa", + "automata", + "automaton", + "nfa" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/regex-automata", + "homepage": "https://github.com/BurntSushi/regex-automata", + "documentation": "https://docs.rs/regex-automata", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.29", + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "ripgrep", + "version": "13.0.0", + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "grep", + "source": null, + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/grep" + }, + { + "name": "ignore", + "source": null, + "req": "^0.4.19", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/ignore" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.23", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "jemallocator", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "rg", + "src_path": "$ROOT$ripgrep/crates/core/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$ripgrep/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "pcre2": [ + "grep/pcre2" + ], + "simd-accel": [ + "grep/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/Cargo.toml", + "metadata": { + "deb": { + "assets": [ + [ + "target/release/rg", + "usr/bin/", + "755" + ], + [ + "COPYING", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "LICENSE-MIT", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "UNLICENSE", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "CHANGELOG.md", + "usr/share/doc/ripgrep/CHANGELOG", + "644" + ], + [ + "README.md", + "usr/share/doc/ripgrep/README", + "644" + ], + [ + "FAQ.md", + "usr/share/doc/ripgrep/FAQ", + "644" + ], + [ + "deployment/deb/rg.1", + "usr/share/man/man1/rg.1", + "644" + ], + [ + "deployment/deb/rg.bash", + "usr/share/bash-completion/completions/rg", + "644" + ], + [ + "deployment/deb/rg.fish", + "usr/share/fish/vendor_completions.d/rg.fish", + "644" + ], + [ + "deployment/deb/_rg", + "usr/share/zsh/vendor-completions/", + "644" + ] + ], + "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n", + "features": [ + "pcre2" + ], + "section": "utils" + } + }, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "command-line-utilities", + "text-processing" + ], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep", + "homepage": "https://github.com/BurntSushi/ripgrep", + "documentation": "https://github.com/BurntSushi/ripgrep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.65" + }, + { + "name": "ryu", + "version": "1.0.13", + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 OR BSL-1.0", + "license_file": null, + "description": "Fast floating point to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ryu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "upstream_benchmark", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "common_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_table_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "exhaustive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "f2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2d_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2f_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ], + "small": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "float" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/ryu", + "homepage": null, + "documentation": "https://docs.rs/ryu", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "same-file", + "version": "1.0.6", + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A simple crate for determining whether two file paths point to the same file.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "same-file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_same_file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_stderr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "same", + "file", + "equal", + "inode" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/same-file", + "homepage": "https://github.com/BurntSushi/same-file", + "documentation": "https://docs.rs/same-file", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar <erick.tryzelaar@gmail.com>", + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar <erick.tryzelaar@gmail.com>", + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "serde_json", + "version": "1.0.96", + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A JSON serialization file format", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "indexmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "itoa", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ryu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "indoc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_stacker", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.49", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde_json", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "debug", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lexical", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "map", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde/alloc" + ], + "arbitrary_precision": [], + "default": [ + "std" + ], + "float_roundtrip": [], + "indexmap": [ + "dep:indexmap" + ], + "preserve_order": [ + "indexmap", + "std" + ], + "raw_value": [], + "std": [ + "serde/std" + ], + "unbounded_depth": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "raw_value", + "unbounded_depth" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "raw_value" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar <erick.tryzelaar@gmail.com>", + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "encoding", + "parser-implementations", + "no-std" + ], + "keywords": [ + "json", + "serde", + "serialization" + ], + "readme": "README.md", + "repository": "https://github.com/serde-rs/json", + "homepage": null, + "documentation": "https://docs.rs/serde_json", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "strsim", + "version": "0.8.0", + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo <dannyguo91@gmail.com>" + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "termcolor", + "version": "1.2.0", + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A simple cross platform library for writing colored text to a terminal.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "termcolor", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [], + "keywords": [ + "windows", + "win", + "color", + "ansi", + "console" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/termcolor", + "homepage": "https://github.com/BurntSushi/termcolor", + "documentation": "https://docs.rs/termcolor", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "textwrap", + "version": "0.11.0", + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hyphenation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "embed_all" + ], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lipsum", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "textwrap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "layout", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "termwidth", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "version-numbers", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "linear", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "hyphenation": [ + "dep:hyphenation" + ], + "term_size": [ + "dep:term_size" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Martin Geisler <martin@geisler.net>" + ], + "categories": [ + "text-processing", + "command-line-interface" + ], + "keywords": [ + "text", + "formatting", + "wrap", + "typesetting", + "hyphenation" + ], + "readme": "README.md", + "repository": "https://github.com/mgeisler/textwrap", + "homepage": null, + "documentation": "https://docs.rs/textwrap/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "thread_local", + "version": "1.1.7", + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Per-object thread-local storage", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "nightly": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Amanieu d'Antras <amanieu@gmail.com>" + ], + "categories": [], + "keywords": [ + "thread_local", + "concurrent", + "thread" + ], + "readme": "README.md", + "repository": "https://github.com/Amanieu/thread_local-rs", + "homepage": null, + "documentation": "https://docs.rs/thread_local/", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay <dtolnay@gmail.com>" + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "unicode-width", + "version": "0.1.10", + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-std", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "std", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-width", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "bench": [], + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "no_std": [], + "rustc-dep-of-std": [ + "std", + "core", + "compiler_builtins" + ], + "std": [ + "dep:std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "kwantam <kwantam@gmail.com>", + "Manish Goregaokar <manishsmail@gmail.com>" + ], + "categories": [], + "keywords": [ + "text", + "width", + "unicode" + ], + "readme": "README.md", + "repository": "https://github.com/unicode-rs/unicode-width", + "homepage": "https://github.com/unicode-rs/unicode-width", + "documentation": "https://unicode-rs.github.io/unicode-width", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "walkdir", + "version": "2.3.3", + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Recursively walk a directory.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "walkdir", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "filesystem" + ], + "keywords": [ + "directory", + "recursive", + "walk", + "iterator" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/walkdir", + "homepage": "https://github.com/BurntSushi/walkdir", + "documentation": "https://docs.rs/walkdir/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-util", + "version": "0.1.5", + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A dumping ground for high level safe wrappers over winapi.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "std", + "consoleapi", + "errhandlingapi", + "fileapi", + "minwindef", + "processenv", + "winbase", + "wincon", + "winerror", + "winnt" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-util", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant <jamslam@gmail.com>" + ], + "categories": [ + "os::windows-apis", + "external-ffi-bindings" + ], + "keywords": [ + "windows", + "winapi", + "util", + "win" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/winapi-util", + "homepage": "https://github.com/BurntSushi/winapi-util", + "documentation": "https://docs.rs/winapi-util", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian <retep998@gmail.com>" + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "perf-literal", + "std" + ] + }, + { + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "hermit_abi", + "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"hermit\")" + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_automata", + "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "std", + "unicode" + ] + }, + { + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "runtime-dispatch-simd" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jobserver", + "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "jobserver", + "parallel" + ] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bitflags", + "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "textwrap", + "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "strsim", + "suggestions" + ] + }, + { + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "crossbeam_utils", + "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "crossbeam-utils", + "default", + "std" + ] + }, + { + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std" + ] + }, + { + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default" + ] + }, + { + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "fnv", + "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "glob", + "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default", + "log" + ] + }, + { + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dependencies": [ + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_cli", + "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_printer", + "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dependencies": [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "atty", + "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "dependencies": [ + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2", + "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dependencies": [ + "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "base64", + "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "base64", + "default", + "serde", + "serde1", + "serde_json" + ] + }, + { + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bytecount", + "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs_io", + "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dependencies": [ + "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "crossbeam_channel", + "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support" + ] + }, + { + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jemalloc_sys", + "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support", + "default" + ] + }, + { + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "alloc", + "default", + "race", + "std" + ] + }, + { + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2_sys", + "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "clap", + "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "grep", + "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ignore", + "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "jemallocator", + "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))" + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "itoa", + "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ryu", + "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "consoleapi", + "errhandlingapi", + "fileapi", + "minwinbase", + "minwindef", + "processenv", + "std", + "winbase", + "wincon", + "winerror", + "winnt" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + }, + "target_directory": "$ROOT$ripgrep/target", + "version": 1, + "workspace_root": "$ROOT$ripgrep", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs index 88db6093ee0..d761a5e798e 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs @@ -242,7 +242,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream let tracing = if let QueryStorage::Memoized | QueryStorage::LruMemoized = query.storage { let s = format!("{trait_name}::{fn_name}"); Some(quote! { - let _p = tracing::debug_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered(); + let _p = tracing::trace_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered(); }) } else { None diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs index 6c5ccba173b..cfe2c48f411 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs @@ -13,7 +13,7 @@ use crate::{Database, DatabaseKeyIndex, Event, EventKind, QueryDb}; use parking_lot::{RawRwLock, RwLock}; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; -use tracing::{debug, info}; +use tracing::trace; pub(super) struct Slot<Q> where @@ -126,7 +126,7 @@ where // doing any `set` invocations while the query function runs. let revision_now = runtime.current_revision(); - info!("{:?}: invoked at {:?}", self, revision_now,); + trace!("{:?}: invoked at {:?}", self, revision_now,); // First, do a check with a read-lock. loop { @@ -152,7 +152,7 @@ where ) -> StampedValue<Q::Value> { let runtime = db.salsa_runtime(); - debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); + trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); // Check with an upgradable read to see if there is a value // already. (This permits other readers but prevents anyone @@ -184,7 +184,7 @@ where // inputs and check whether they are out of date. if let Some(memo) = &mut old_memo { if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) { - info!("{:?}: validated old memoized value", self,); + trace!("{:?}: validated old memoized value", self,); db.salsa_event(Event { runtime_id: runtime.id(), @@ -212,7 +212,7 @@ where old_memo: Option<Memo<Q::Value>>, key: &Q::Key, ) -> StampedValue<Q::Value> { - tracing::info!("{:?}: executing query", self.database_key_index().debug(db)); + tracing::trace!("{:?}: executing query", self.database_key_index().debug(db)); db.salsa_event(Event { runtime_id: db.salsa_runtime().id(), @@ -224,7 +224,7 @@ where let value = match Cycle::catch(|| Q::execute(db, key.clone())) { Ok(v) => v, Err(cycle) => { - tracing::debug!( + tracing::trace!( "{:?}: caught cycle {:?}, have strategy {:?}", self.database_key_index().debug(db), cycle, @@ -272,9 +272,10 @@ where // consumers must be aware of. Becoming *more* durable // is not. See the test `constant_to_non_constant`. if revisions.durability >= old_memo.revisions.durability && old_memo.value == value { - debug!( + trace!( "read_upgrade({:?}): value is equal, back-dating to {:?}", - self, old_memo.revisions.changed_at, + self, + old_memo.revisions.changed_at, ); assert!(old_memo.revisions.changed_at <= revisions.changed_at); @@ -290,7 +291,7 @@ where let memo_value = new_value.value.clone(); - debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); + trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions })); @@ -339,9 +340,11 @@ where } QueryState::Memoized(memo) => { - debug!( + trace!( "{:?}: found memoized value, verified_at={:?}, changed_at={:?}", - self, memo.verified_at, memo.revisions.changed_at, + self, + memo.verified_at, + memo.revisions.changed_at, ); if memo.verified_at < revision_now { @@ -355,7 +358,7 @@ where value: value.clone(), }; - info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); + trace!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); ProbeState::UpToDate(value) } @@ -387,7 +390,7 @@ where } pub(super) fn invalidate(&self, new_revision: Revision) -> Option<Durability> { - tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision); + tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision); match &mut *self.state.write() { QueryState::Memoized(memo) => { memo.revisions.untracked = true; @@ -411,9 +414,11 @@ where db.unwind_if_cancelled(); - debug!( + trace!( "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}", - self, revision, revision_now, + self, + revision, + revision_now, ); // Do an initial probe with just the read-lock. @@ -680,9 +685,11 @@ where assert!(self.verified_at != revision_now); let verified_at = self.verified_at; - debug!( + trace!( "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}", - verified_at, revision_now, self.revisions.inputs + verified_at, + revision_now, + self.revisions.inputs ); if self.check_durability(db.salsa_runtime()) { @@ -708,7 +715,7 @@ where let changed_input = inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at)); if let Some(input) = changed_input { - debug!("validate_memoized_value: `{:?}` may have changed", input); + trace!("validate_memoized_value: `{:?}` may have changed", input); return false; } @@ -721,7 +728,7 @@ where /// True if this memo is known not to have changed based on its durability. fn check_durability(&self, runtime: &Runtime) -> bool { let last_changed = runtime.last_changed_revision(self.revisions.durability); - debug!( + trace!( "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}", last_changed, self.verified_at, diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs index ff9cc4eade2..73a5e07aa05 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs @@ -17,7 +17,7 @@ use parking_lot::{RawRwLock, RwLock}; use std::marker::PhantomData; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; -use tracing::{debug, info}; +use tracing::trace; pub(super) struct Slot<Q, MP> where @@ -140,7 +140,7 @@ where // doing any `set` invocations while the query function runs. let revision_now = runtime.current_revision(); - info!("{:?}: invoked at {:?}", self, revision_now,); + trace!("{:?}: invoked at {:?}", self, revision_now,); // First, do a check with a read-lock. loop { @@ -168,7 +168,7 @@ where ) -> StampedValue<Q::Value> { let runtime = db.salsa_runtime(); - debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); + trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); // Check with an upgradable read to see if there is a value // already. (This permits other readers but prevents anyone @@ -202,7 +202,7 @@ where // inputs and check whether they are out of date. if let Some(memo) = &mut old_memo { if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) { - info!("{:?}: validated old memoized value", self,); + trace!("{:?}: validated old memoized value", self,); db.salsa_event(Event { runtime_id: runtime.id(), @@ -230,7 +230,7 @@ where old_memo: Option<Memo<Q::Value>>, key: &Q::Key, ) -> StampedValue<Q::Value> { - tracing::info!("{:?}: executing query", self.database_key_index().debug(db)); + tracing::trace!("{:?}: executing query", self.database_key_index().debug(db)); db.salsa_event(Event { runtime_id: db.salsa_runtime().id(), @@ -242,7 +242,7 @@ where let value = match Cycle::catch(|| Q::execute(db, key.clone())) { Ok(v) => v, Err(cycle) => { - tracing::debug!( + tracing::trace!( "{:?}: caught cycle {:?}, have strategy {:?}", self.database_key_index().debug(db), cycle, @@ -293,9 +293,10 @@ where if revisions.durability >= old_memo.revisions.durability && MP::memoized_value_eq(old_value, &value) { - debug!( + trace!( "read_upgrade({:?}): value is equal, back-dating to {:?}", - self, old_memo.revisions.changed_at, + self, + old_memo.revisions.changed_at, ); assert!(old_memo.revisions.changed_at <= revisions.changed_at); @@ -313,7 +314,7 @@ where let memo_value = if self.should_memoize_value(key) { Some(new_value.value.clone()) } else { None }; - debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); + trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions })); @@ -362,9 +363,11 @@ where } QueryState::Memoized(memo) => { - debug!( + trace!( "{:?}: found memoized value, verified_at={:?}, changed_at={:?}", - self, memo.verified_at, memo.revisions.changed_at, + self, + memo.verified_at, + memo.revisions.changed_at, ); if memo.verified_at < revision_now { @@ -378,7 +381,11 @@ where value: value.clone(), }; - info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); + trace!( + "{:?}: returning memoized value changed at {:?}", + self, + value.changed_at + ); ProbeState::UpToDate(value) } else { @@ -426,7 +433,7 @@ where } pub(super) fn invalidate(&self, new_revision: Revision) -> Option<Durability> { - tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision); + tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision); match &mut *self.state.write() { QueryState::Memoized(memo) => { memo.revisions.untracked = true; @@ -450,9 +457,11 @@ where db.unwind_if_cancelled(); - debug!( + trace!( "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}", - self, revision, revision_now, + self, + revision, + revision_now, ); // Do an initial probe with just the read-lock. @@ -734,9 +743,11 @@ where assert!(self.verified_at != revision_now); let verified_at = self.verified_at; - debug!( + trace!( "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}", - verified_at, revision_now, self.revisions.inputs + verified_at, + revision_now, + self.revisions.inputs ); if self.check_durability(db.salsa_runtime()) { @@ -762,7 +773,7 @@ where let changed_input = inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at)); if let Some(input) = changed_input { - debug!("validate_memoized_value: `{:?}` may have changed", input); + trace!("validate_memoized_value: `{:?}` may have changed", input); return false; } @@ -775,7 +786,7 @@ where /// True if this memo is known not to have changed based on its durability. fn check_durability(&self, runtime: &Runtime) -> bool { let last_changed = runtime.last_changed_revision(self.revisions.durability); - debug!( + trace!( "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}", last_changed, self.verified_at, diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs index f04f48e3bab..4992a0c7271 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs @@ -14,7 +14,7 @@ use crate::{DatabaseKeyIndex, QueryDb}; use indexmap::map::Entry; use parking_lot::RwLock; use std::iter; -use tracing::debug; +use tracing::trace; /// Input queries store the result plus a list of the other queries /// that they invoked. This means we can avoid recomputing them when @@ -73,11 +73,11 @@ where return true; }; - debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); + trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); let changed_at = slot.stamped_value.read().changed_at; - debug!("maybe_changed_after: changed_at = {:?}", changed_at); + trace!("maybe_changed_after: changed_at = {:?}", changed_at); changed_at > revision } @@ -140,7 +140,7 @@ where Q: Query, { fn set(&self, runtime: &mut Runtime, key: &Q::Key, value: Q::Value, durability: Durability) { - tracing::debug!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability); + tracing::trace!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability); // The value is changing, so we need a new revision (*). We also // need to update the 'last changed' revision by invoking @@ -234,14 +234,14 @@ where ) -> bool { debug_assert!(revision < db.salsa_runtime().current_revision()); - debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); + trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); let Some(value) = &*self.slot.stamped_value.read() else { return true; }; let changed_at = value.changed_at; - debug!("maybe_changed_after: changed_at = {:?}", changed_at); + trace!("maybe_changed_after: changed_at = {:?}", changed_at); changed_at > revision } @@ -298,7 +298,7 @@ where Q: Query<Key = ()>, { fn set(&self, runtime: &mut Runtime, (): &Q::Key, value: Q::Value, durability: Durability) { - tracing::debug!("{:?} = {:?} ({:?})", Q::default(), value, durability); + tracing::trace!("{:?} = {:?} ({:?})", Q::default(), value, durability); // The value is changing, so we need a new revision (*). We also // need to update the 'last changed' revision by invoking diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs index 8530521d915..843b6d31f0c 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs @@ -79,7 +79,7 @@ pub trait Database: plumbing::DatabaseOps { let current_revision = runtime.current_revision(); let pending_revision = runtime.pending_revision(); - tracing::debug!( + tracing::trace!( "unwind_if_cancelled: current_revision={:?}, pending_revision={:?}", current_revision, pending_revision @@ -684,7 +684,7 @@ impl Cycle { } pub(crate) fn throw(self) -> ! { - tracing::debug!("throwing cycle {:?}", self); + tracing::trace!("throwing cycle {:?}", self); std::panic::resume_unwind(Box::new(self)) } diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs index a6f96beeab1..7fbd42f9262 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs @@ -103,11 +103,11 @@ where /// Records that `node` was used. This may displace an old node (if the LRU limits are pub(crate) fn record_use(&self, node: &Arc<Node>) -> Option<Arc<Node>> { - tracing::debug!("record_use(node={:?})", node); + tracing::trace!("record_use(node={:?})", node); // Load green zone length and check if the LRU cache is even enabled. let green_zone = self.green_zone.load(Ordering::Acquire); - tracing::debug!("record_use: green_zone={}", green_zone); + tracing::trace!("record_use: green_zone={}", green_zone); if green_zone == 0 { return None; } @@ -115,7 +115,7 @@ where // Find current index of list (if any) and the current length // of our green zone. let index = node.lru_index().load(); - tracing::debug!("record_use: index={}", index); + tracing::trace!("record_use: index={}", index); // Already a member of the list, and in the green zone -- nothing to do! if index < green_zone { @@ -162,9 +162,9 @@ where let entries = std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone as usize)); - tracing::debug!("green_zone = {:?}", self.green_zone()); - tracing::debug!("yellow_zone = {:?}", self.yellow_zone()); - tracing::debug!("red_zone = {:?}", self.red_zone()); + tracing::trace!("green_zone = {:?}", self.green_zone()); + tracing::trace!("yellow_zone = {:?}", self.yellow_zone()); + tracing::trace!("red_zone = {:?}", self.red_zone()); // We expect to resize when the LRU cache is basically empty. // So just forget all the old LRU indices to start. @@ -180,7 +180,7 @@ where /// list may displace an old member of the red zone, in which case /// that is returned. fn record_use(&mut self, node: &Arc<Node>) -> Option<Arc<Node>> { - tracing::debug!("record_use(node={:?})", node); + tracing::trace!("record_use(node={:?})", node); // NB: When this is invoked, we have typically already loaded // the LRU index (to check if it is in green zone). But that @@ -212,7 +212,7 @@ where if len < self.end_red_zone { self.entries.push(node.clone()); node.lru_index().store(len); - tracing::debug!("inserted node {:?} at {}", node, len); + tracing::trace!("inserted node {:?} at {}", node, len); return self.record_use(node); } @@ -220,7 +220,7 @@ where // zone and then promoting. let victim_index = self.pick_index(self.red_zone()); let victim_node = std::mem::replace(&mut self.entries[victim_index as usize], node.clone()); - tracing::debug!("evicting red node {:?} from {}", victim_node, victim_index); + tracing::trace!("evicting red node {:?} from {}", victim_node, victim_index); victim_node.lru_index().clear(); self.promote_red_to_green(node, victim_index); Some(victim_node) @@ -241,7 +241,7 @@ where // going to invoke `self.promote_yellow` next, and it will get // updated then. let yellow_index = self.pick_index(self.yellow_zone()); - tracing::debug!( + tracing::trace!( "demoting yellow node {:?} from {} to red at {}", self.entries[yellow_index as usize], yellow_index, @@ -265,7 +265,7 @@ where // Pick a yellow at random and switch places with it. let green_index = self.pick_index(self.green_zone()); - tracing::debug!( + tracing::trace!( "demoting green node {:?} from {} to yellow at {}", self.entries[green_index as usize], green_index, @@ -275,7 +275,7 @@ where self.entries[yellow_index as usize].lru_index().store(yellow_index); node.lru_index().store(green_index); - tracing::debug!("promoted {:?} to green index {}", node, green_index); + tracing::trace!("promoted {:?} to green index {}", node, green_index); } fn pick_index(&mut self, zone: std::ops::Range<u16>) -> u16 { diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs index 5fe5f4b46d3..cb16ba0044d 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs @@ -9,7 +9,7 @@ use parking_lot::{Mutex, RwLock}; use std::hash::Hash; use std::panic::panic_any; use std::sync::atomic::{AtomicU32, Ordering}; -use tracing::debug; +use tracing::trace; use triomphe::{Arc, ThinArc}; mod dependency_graph; @@ -177,7 +177,7 @@ impl Runtime { where F: FnOnce(Revision) -> Option<Durability>, { - tracing::debug!("increment_revision()"); + tracing::trace!("increment_revision()"); if !self.permits_increment() { panic!("increment_revision invoked during a query computation"); @@ -196,7 +196,7 @@ impl Runtime { let new_revision = current_revision.next(); - debug!("increment_revision: incremented to {:?}", new_revision); + trace!("increment_revision: incremented to {:?}", new_revision); if let Some(d) = op(new_revision) { for rev in &self.shared_state.revisions[1..=d.index()] { @@ -267,7 +267,7 @@ impl Runtime { database_key_index: DatabaseKeyIndex, to_id: RuntimeId, ) { - debug!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index); + trace!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index); let mut from_stack = self.local_state.take_query_stack(); let from_id = self.id(); @@ -305,7 +305,7 @@ impl Runtime { Cycle::new(Arc::new(v)) }; - debug!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,); + trace!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,); // We can remove the cycle participants from the list of dependencies; // they are a strongly connected component (SCC) and we only care about @@ -323,7 +323,7 @@ impl Runtime { CycleRecoveryStrategy::Fallback => false, }) .for_each(|aq| { - debug!("marking {:?} for fallback", aq.database_key_index.debug(db)); + trace!("marking {:?} for fallback", aq.database_key_index.debug(db)); aq.take_inputs_from(&cycle_query); assert!(aq.cycle.is_none()); aq.cycle = Some(cycle.clone()); diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs index 73869671886..4ab4bad0cc5 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs @@ -1,4 +1,4 @@ -use tracing::debug; +use tracing::trace; use triomphe::ThinArc; use crate::durability::Durability; @@ -78,7 +78,7 @@ impl LocalState { durability: Durability, changed_at: Revision, ) { - debug!( + trace!( "report_query_read_and_unwind_if_cycle_resulted(input={:?}, durability={:?}, changed_at={:?})", input, durability, changed_at ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index fa9ff6b56df..d06130ce8c5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -76,7 +76,7 @@ vfs.workspace = true paths.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_System_Threading"] } +windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Threading"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs index 72b741de00e..0fd381d6122 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs @@ -32,6 +32,7 @@ fn set_rerun() { } fn set_commit_info() { + #[allow(clippy::disallowed_methods)] let output = match Command::new("git") .arg("log") .arg("-1") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index a753621eca8..1a9cdef256d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -44,11 +44,7 @@ fn actual_main() -> anyhow::Result<ExitCode> { #[cfg(debug_assertions)] if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { - #[allow(unused_mut)] - let mut d = 4; - while d == 4 { - d = 4; - } + wait_for_debugger(); } if let Err(e) = setup_logging(flags.log_file.clone()) { @@ -96,6 +92,28 @@ fn actual_main() -> anyhow::Result<ExitCode> { Ok(ExitCode::SUCCESS) } +#[cfg(debug_assertions)] +fn wait_for_debugger() { + #[cfg(target_os = "windows")] + { + use windows_sys::Win32::System::Diagnostics::Debug::IsDebuggerPresent; + // SAFETY: WinAPI generated code that is defensively marked `unsafe` but + // in practice can not be used in an unsafe way. + while unsafe { IsDebuggerPresent() } == 0 { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + #[cfg(not(target_os = "windows"))] + { + #[allow(unused_mut)] + let mut d = 4; + while d == 4 { + d = 4; + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } +} + fn setup_logging(log_file_flag: Option<PathBuf>) -> anyhow::Result<()> { if cfg!(windows) { // This is required so that windows finds our pdb that is placed right beside the exe. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs index 684b3f52afc..b9fcd2e1870 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs @@ -46,6 +46,7 @@ fn run_rustc_skipping_cargo_checking( } fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> { + #[allow(clippy::disallowed_methods)] let mut child = Command::new(rustc_executable) .args(args) .stdin(Stdio::inherit()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 66cd2e424e3..afe3455b780 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -66,10 +66,6 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, - sysroot_query_metadata: match self.no_query_sysroot_metadata { - true => project_model::SysrootQueryMetadata::None, - false => project_model::SysrootQueryMetadata::CargoMetadata, - }, all_targets: true, set_test: !self.no_test, cfg_overrides: CfgOverrides { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 920a2a37efb..ff24602144a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -71,9 +71,6 @@ xflags::xflags! { optional --with-deps /// Don't load sysroot crates (`std`, `core` & friends). optional --no-sysroot - /// Don't run cargo metadata on the sysroot to analyze its third-party dependencies. - /// Requires --no-sysroot to not be set. - optional --no-query-sysroot-metadata /// Don't set #[cfg(test)]. optional --no-test @@ -238,7 +235,6 @@ pub struct AnalysisStats { pub only: Option<String>, pub with_deps: bool, pub no_sysroot: bool, - pub no_query_sysroot_metadata: bool, pub no_test: bool, pub disable_build_scripts: bool, pub disable_proc_macros: bool, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 33c4f31fbee..eb5c44418b7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -4,8 +4,9 @@ use std::env; use std::time::Instant; use ide::{ - Analysis, AnalysisHost, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, - StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig, + Analysis, AnalysisHost, FileId, FileRange, MonikerKind, MonikerResult, PackageInformation, + RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, + VendoredLibrariesConfig, }; use ide_db::{line_index::WideEncoding, LineIndexDatabase}; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -167,7 +168,7 @@ impl LsifManager<'_, '_> { out_v: result_set_id.into(), })); } - if let Some(moniker) = token.moniker { + if let Some(MonikerResult::Moniker(moniker)) = token.moniker { let package_id = self.get_package_id(moniker.package_information); let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker { scheme: "rust-analyzer".to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 6483afc85b2..6b0ce4db7c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -10,10 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use itertools::Either; use paths::Utf8PathBuf; use profile::StopWatch; -use project_model::target_data_layout::RustcDataLayoutConfig; +use project_model::toolchain_info::{target_data_layout, QueryConfig}; use project_model::{ - target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, - RustLibSource, Sysroot, SysrootQueryMetadata, + CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot, + SysrootSourceWorkspaceConfig, }; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -74,13 +74,10 @@ impl Tester { ..Default::default() }; - let sysroot = Sysroot::discover( - tmp_file.parent().unwrap(), - &cargo_config.extra_env, - SysrootQueryMetadata::CargoMetadata, - ); + let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), + QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), None, &cargo_config.extra_env, ); @@ -89,7 +86,6 @@ impl Tester { kind: ProjectWorkspaceKind::DetachedFile { file: ManifestPath::try_from(tmp_file).unwrap(), cargo: None, - cargo_config_extra_env: Default::default(), set_test: true, }, sysroot, @@ -302,7 +298,7 @@ impl flags::RustcTests { continue; } } - if p.extension().map_or(true, |x| x != "rs") { + if p.extension().is_none_or(|x| x != "rs") { continue; } if let Err(e) = std::panic::catch_unwind({ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index ff009e69547..6ca7d9ac057 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -3,14 +3,16 @@ use std::{path::PathBuf, time::Instant}; use ide::{ - AnalysisHost, LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, - SymbolInformationKind, TextRange, TokenId, VendoredLibrariesConfig, + AnalysisHost, LineCol, Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerResult, + RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId, + TokenStaticData, VendoredLibrariesConfig, }; use ide_db::LineIndexDatabase; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use rustc_hash::{FxHashMap, FxHashSet}; -use scip::types as scip_types; +use scip::types::{self as scip_types, SymbolInformation}; use tracing::error; +use vfs::FileId; use crate::{ cli::flags, @@ -83,32 +85,56 @@ impl flags::Scip { text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), }; - let mut documents = Vec::new(); - - let mut symbols_emitted: FxHashSet<TokenId> = FxHashSet::default(); - let mut tokens_to_symbol: FxHashMap<TokenId, String> = FxHashMap::default(); - let mut tokens_to_enclosing_symbol: FxHashMap<TokenId, Option<String>> = - FxHashMap::default(); - for StaticIndexedFile { file_id, tokens, .. } in si.files { - let mut local_count = 0; - let mut new_local_symbol = || { - let new_symbol = scip::types::Symbol::new_local(local_count); - local_count += 1; + let mut documents = Vec::new(); - new_symbol + // All TokenIds where an Occurrence has been emitted that references a symbol. + let mut token_ids_referenced: FxHashSet<TokenId> = FxHashSet::default(); + // All TokenIds where the SymbolInformation has been written to the document. + let mut token_ids_emitted: FxHashSet<TokenId> = FxHashSet::default(); + // All FileIds emitted as documents. + let mut file_ids_emitted: FxHashSet<FileId> = FxHashSet::default(); + + // All non-local symbols encountered, for detecting duplicate symbol errors. + let mut nonlocal_symbols_emitted: FxHashSet<String> = FxHashSet::default(); + // List of (source_location, symbol) for duplicate symbol errors to report. + let mut duplicate_symbol_errors: Vec<(String, String)> = Vec::new(); + // This is called after definitions have been deduplicated by token_ids_emitted. The purpose + // is to detect reuse of symbol names because this causes ambiguity about their meaning. + let mut record_error_if_symbol_already_used = + |symbol: String, + is_inherent_impl: bool, + relative_path: &str, + line_index: &LineIndex, + text_range: TextRange| { + let is_local = symbol.starts_with("local "); + if !is_local && !nonlocal_symbols_emitted.insert(symbol.clone()) { + if is_inherent_impl { + // FIXME: See #18772. Duplicate SymbolInformation for inherent impls is + // omitted. It would be preferable to emit them with numbers with + // disambiguation, but this is more complex to implement. + false + } else { + let source_location = + text_range_to_string(relative_path, line_index, text_range); + duplicate_symbol_errors.push((source_location, symbol)); + // Keep duplicate SymbolInformation. This behavior is preferred over + // omitting so that the issue might be visible within downstream tools. + true + } + } else { + true + } }; - let relative_path = match get_relative_filepath(&vfs, &root, file_id) { - Some(relative_path) => relative_path, - None => continue, - }; + // Generates symbols from token monikers. + let mut symbol_generator = SymbolGenerator::new(); - let line_index = LineIndex { - index: db.line_index(file_id), - encoding: PositionEncoding::Utf8, - endings: LineEndings::Unix, - }; + for StaticIndexedFile { file_id, tokens, .. } in si.files { + symbol_generator.clear_document_local_state(); + + let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue }; + let line_index = get_line_index(db, file_id); let mut occurrences = Vec::new(); let mut symbols = Vec::new(); @@ -116,71 +142,58 @@ impl flags::Scip { tokens.into_iter().for_each(|(text_range, id)| { let token = si.tokens.get(id).unwrap(); - let range = text_range_to_scip_range(&line_index, text_range); - let symbol = tokens_to_symbol - .entry(id) - .or_insert_with(|| { - let symbol = token - .moniker - .as_ref() - .map(moniker_to_symbol) - .unwrap_or_else(&mut new_local_symbol); - scip::symbol::format_symbol(symbol) - }) - .clone(); - let enclosing_symbol = tokens_to_enclosing_symbol - .entry(id) - .or_insert_with(|| { - token - .enclosing_moniker - .as_ref() - .map(moniker_to_symbol) - .map(scip::symbol::format_symbol) - }) - .clone(); - - let mut symbol_roles = Default::default(); - - if let Some(def) = token.definition { - // if the range of the def and the range of the token are the same, this must be the definition. - // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 - if def.file_id == file_id && def.range == text_range { - symbol_roles |= scip_types::SymbolRole::Definition as i32; + let (symbol, enclosing_symbol, is_inherent_impl) = + if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = + symbol_generator.token_symbols(id, token) + { + (symbol, enclosing_symbol, is_inherent_impl) + } else { + ("".to_owned(), None, false) + }; + + if !symbol.is_empty() { + let is_defined_in_this_document = match token.definition { + Some(def) => def.file_id == file_id, + _ => false, + }; + if is_defined_in_this_document { + if token_ids_emitted.insert(id) { + // token_ids_emitted does deduplication. This checks that this results + // in unique emitted symbols, as otherwise references are ambiguous. + let should_emit = record_error_if_symbol_already_used( + symbol.clone(), + is_inherent_impl, + relative_path.as_str(), + &line_index, + text_range, + ); + if should_emit { + symbols.push(compute_symbol_info( + symbol.clone(), + enclosing_symbol, + token, + )); + } + } + } else { + token_ids_referenced.insert(id); } + } - if symbols_emitted.insert(id) { - let documentation = match &token.documentation { - Some(doc) => vec![doc.as_str().to_owned()], - None => vec![], - }; - - let position_encoding = - scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into(); - let signature_documentation = - token.signature.clone().map(|text| scip_types::Document { - relative_path: relative_path.clone(), - language: "rust".to_owned(), - text, - position_encoding, - ..Default::default() - }); - let symbol_info = scip_types::SymbolInformation { - symbol: symbol.clone(), - documentation, - relationships: Vec::new(), - special_fields: Default::default(), - kind: symbol_kind(token.kind).into(), - display_name: token.display_name.clone().unwrap_or_default(), - signature_documentation: signature_documentation.into(), - enclosing_symbol: enclosing_symbol.unwrap_or_default(), - }; - - symbols.push(symbol_info) - } + // If the range of the def and the range of the token are the same, this must be the definition. + // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 + let is_definition = match token.definition { + Some(def) => def.file_id == file_id && def.range == text_range, + _ => false, + }; + + let mut symbol_roles = Default::default(); + if is_definition { + symbol_roles |= scip_types::SymbolRole::Definition as i32; } occurrences.push(scip_types::Occurrence { - range, + range: text_range_to_scip_range(&line_index, text_range), symbol, symbol_roles, override_documentation: Vec::new(), @@ -206,15 +219,63 @@ impl flags::Scip { position_encoding, special_fields: Default::default(), }); + if !file_ids_emitted.insert(file_id) { + panic!("Invariant violation: file emitted multiple times."); + } + } + + // Collect all symbols referenced by the files but not defined within them. + let mut external_symbols = Vec::new(); + for id in token_ids_referenced.difference(&token_ids_emitted) { + let id = *id; + let token = si.tokens.get(id).unwrap(); + + let Some(definition) = token.definition else { + break; + }; + + let file_id = definition.file_id; + let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue }; + let line_index = get_line_index(db, file_id); + let text_range = definition.range; + if file_ids_emitted.contains(&file_id) { + tracing::error!( + "Bug: definition at {} should have been in an SCIP document but was not.", + text_range_to_string(relative_path.as_str(), &line_index, text_range) + ); + continue; + } + + let TokenSymbols { symbol, enclosing_symbol, .. } = symbol_generator + .token_symbols(id, token) + .expect("To have been referenced, the symbol must be in the cache."); + + record_error_if_symbol_already_used( + symbol.clone(), + false, + relative_path.as_str(), + &line_index, + text_range, + ); + external_symbols.push(compute_symbol_info(symbol.clone(), enclosing_symbol, token)); } let index = scip_types::Index { metadata: Some(metadata).into(), documents, - external_symbols: Vec::new(), + external_symbols, special_fields: Default::default(), }; + if !duplicate_symbol_errors.is_empty() { + eprintln!("{}", DUPLICATE_SYMBOLS_MESSAGE); + for (source_location, symbol) in duplicate_symbol_errors { + eprintln!("{}", source_location); + eprintln!(" Duplicate symbol: {}", symbol); + eprintln!(); + } + } + let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip")); scip::write_message_to_file(out_path, index) .map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?; @@ -224,6 +285,53 @@ impl flags::Scip { } } +// FIXME: Known buggy cases are described here. +const DUPLICATE_SYMBOLS_MESSAGE: &str = " +Encountered duplicate scip symbols, indicating an internal rust-analyzer bug. These duplicates are +included in the output, but this causes information lookup to be ambiguous and so information about +these symbols presented by downstream tools may be incorrect. + +Known rust-analyzer bugs that can cause this: + + * Definitions in crate example binaries which have the same symbol as definitions in the library + or some other example. + + * Struct/enum/const/static/impl definitions nested in a function do not mention the function name. + See #18771. + +Duplicate symbols encountered: +"; + +fn compute_symbol_info( + symbol: String, + enclosing_symbol: Option<String>, + token: &TokenStaticData, +) -> SymbolInformation { + let documentation = match &token.documentation { + Some(doc) => vec![doc.as_str().to_owned()], + None => vec![], + }; + + let position_encoding = scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into(); + let signature_documentation = token.signature.clone().map(|text| scip_types::Document { + relative_path: "".to_owned(), + language: "rust".to_owned(), + text, + position_encoding, + ..Default::default() + }); + scip_types::SymbolInformation { + symbol, + documentation, + relationships: Vec::new(), + special_fields: Default::default(), + kind: symbol_kind(token.kind).into(), + display_name: token.display_name.clone().unwrap_or_default(), + signature_documentation: signature_documentation.into(), + enclosing_symbol: enclosing_symbol.unwrap_or_default(), + } +} + fn get_relative_filepath( vfs: &vfs::Vfs, rootpath: &vfs::AbsPathBuf, @@ -232,6 +340,14 @@ fn get_relative_filepath( Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned()) } +fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex { + LineIndex { + index: db.line_index(file_id), + encoding: PositionEncoding::Utf8, + endings: LineEndings::Unix, + } +} + // SCIP Ranges have a (very large) optimization that ranges if they are on the same line // only encode as a vector of [start_line, start_col, end_col]. // @@ -247,6 +363,13 @@ fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32 } } +fn text_range_to_string(relative_path: &str, line_index: &LineIndex, range: TextRange) -> String { + let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start()); + let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end()); + + format!("{relative_path}:{start_line}:{start_col}-{end_line}:{end_col}") +} + fn new_descriptor_str( name: &str, suffix: scip_types::descriptor::Suffix, @@ -259,14 +382,6 @@ fn new_descriptor_str( } } -fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { - if name.contains('\'') { - new_descriptor_str(&format!("`{name}`"), suffix) - } else { - new_descriptor_str(name, suffix) - } -} - fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind { use scip_types::symbol_information::Kind as ScipKind; match kind { @@ -295,17 +410,91 @@ fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::K } } -fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { - use scip_types::descriptor::Suffix::*; +#[derive(Clone)] +struct TokenSymbols { + symbol: String, + /// Definition that contains this one. Only set when `symbol` is local. + enclosing_symbol: Option<String>, + /// True if this symbol is for an inherent impl. This is used to only emit `SymbolInformation` + /// for a struct's first inherent impl, since their symbol names are not disambiguated. + is_inherent_impl: bool, +} + +struct SymbolGenerator { + token_to_symbols: FxHashMap<TokenId, Option<TokenSymbols>>, + local_count: usize, +} + +impl SymbolGenerator { + fn new() -> Self { + SymbolGenerator { token_to_symbols: FxHashMap::default(), local_count: 0 } + } + + fn clear_document_local_state(&mut self) { + self.local_count = 0; + } + + fn token_symbols(&mut self, id: TokenId, token: &TokenStaticData) -> Option<TokenSymbols> { + let mut local_count = self.local_count; + let token_symbols = self + .token_to_symbols + .entry(id) + .or_insert_with(|| { + Some(match token.moniker.as_ref()? { + MonikerResult::Moniker(moniker) => TokenSymbols { + symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)), + enclosing_symbol: None, + is_inherent_impl: moniker + .identifier + .description + .get(moniker.identifier.description.len() - 2) + .is_some_and(|descriptor| { + descriptor.desc == MonikerDescriptorKind::Type + && descriptor.name == "impl" + }), + }, + MonikerResult::Local { enclosing_moniker } => { + let local_symbol = scip::types::Symbol::new_local(local_count); + local_count += 1; + TokenSymbols { + symbol: scip::symbol::format_symbol(local_symbol), + enclosing_symbol: enclosing_moniker + .as_ref() + .map(moniker_to_symbol) + .map(scip::symbol::format_symbol), + is_inherent_impl: false, + } + } + }) + }) + .clone(); + self.local_count = local_count; + token_symbols + } +} + +fn moniker_to_symbol(moniker: &Moniker) -> scip_types::Symbol { + scip_types::Symbol { + scheme: "rust-analyzer".into(), + package: Some(scip_types::Package { + manager: "cargo".to_owned(), + name: moniker.package_information.name.clone(), + version: moniker.package_information.version.clone().unwrap_or_else(|| ".".to_owned()), + special_fields: Default::default(), + }) + .into(), + descriptors: moniker_descriptors(&moniker.identifier), + special_fields: Default::default(), + } +} - let package_name = moniker.package_information.name.clone(); - let version = moniker.package_information.version.clone(); - let descriptors = moniker - .identifier +fn moniker_descriptors(identifier: &MonikerIdentifier) -> Vec<scip_types::Descriptor> { + use scip_types::descriptor::Suffix::*; + identifier .description .iter() .map(|desc| { - new_descriptor( + new_descriptor_str( &desc.name, match desc.desc { MonikerDescriptorKind::Namespace => Namespace, @@ -319,27 +508,13 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { }, ) }) - .collect(); - - scip_types::Symbol { - scheme: "rust-analyzer".into(), - package: Some(scip_types::Package { - manager: "cargo".to_owned(), - name: package_name, - version: version.unwrap_or_else(|| ".".to_owned()), - special_fields: Default::default(), - }) - .into(), - descriptors, - special_fields: Default::default(), - } + .collect() } #[cfg(test)] mod test { use super::*; use ide::{FilePosition, TextSize}; - use scip::symbol::format_symbol; use test_fixture::ChangeFixture; use vfs::VfsPath; @@ -376,7 +551,21 @@ mod test { for &(range, id) in &file.tokens { if range.contains(offset - TextSize::from(1)) { let token = si.tokens.get(id).unwrap(); - found_symbol = token.moniker.as_ref().map(moniker_to_symbol); + found_symbol = match token.moniker.as_ref() { + None => None, + Some(MonikerResult::Moniker(moniker)) => { + Some(scip::symbol::format_symbol(moniker_to_symbol(moniker))) + } + Some(MonikerResult::Local { enclosing_moniker: Some(moniker) }) => { + Some(format!( + "local enclosed by {}", + scip::symbol::format_symbol(moniker_to_symbol(moniker)) + )) + } + Some(MonikerResult::Local { enclosing_moniker: None }) => { + Some("unenclosed local".to_owned()) + } + }; break; } } @@ -388,9 +577,7 @@ mod test { } assert!(found_symbol.is_some(), "must have one symbol {found_symbol:?}"); - let res = found_symbol.unwrap(); - let formatted = format_symbol(res); - assert_eq!(formatted, expected); + assert_eq!(found_symbol.unwrap(), expected); } #[test] @@ -467,8 +654,7 @@ pub mod module { } } "#, - // "foo::module::MyTrait::MyType", - "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]", + "rust-analyzer cargo foo 0.1.0 module/MyTrait#MyType#", ); } @@ -489,8 +675,7 @@ pub mod module { } } "#, - // "foo::module::MyStruct::MyTrait::func", - "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().", + "rust-analyzer cargo foo 0.1.0 module/impl#[MyStruct][MyTrait]func().", ); } @@ -526,7 +711,7 @@ pub mod example_mod { pub fn func(x$0: usize) {} } "#, - "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)", + "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().", ); } @@ -546,7 +731,7 @@ pub mod example_mod { } } "#, - "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)", + "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().", ); } @@ -566,7 +751,7 @@ pub mod example_mod { } } "#, - "", + "local enclosed by rust-analyzer cargo foo 0.1.0 module/func().", ); } @@ -609,7 +794,7 @@ pub mod example_mod { } #[test] - fn symbol_for_for_type_alias() { + fn symbol_for_type_alias() { check_symbol( r#" //- /workspace/lib.rs crate:main @@ -619,6 +804,70 @@ pub mod example_mod { ); } + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_nested_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + pub fn inner_func$0() {} + } + "#, + "rust-analyzer cargo main . inner_func().", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_struct_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + struct SomeStruct$0 {} + } + "#, + "rust-analyzer cargo main . SomeStruct#", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_const_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + const SOME_CONST$0: u32 = 1; + } + "#, + "rust-analyzer cargo main . SOME_CONST.", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_static_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + static SOME_STATIC$0: u32 = 1; + } + "#, + "rust-analyzer cargo main . SOME_STATIC.", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + #[test] fn documentation_matches_doc_comment() { let s = "/// foo\nfn bar() {}"; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index b06117f7383..30f0031905f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -50,6 +50,14 @@ mod patch_old_style; // - Don't use abbreviations unless really necessary // - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum MaxSubstitutionLength { + Hide, + #[serde(untagged)] + Limit(usize), +} + // Defines the server-side configuration of the rust-analyzer. We generate // *parts* of VS Code's `package.json` config from this. Run `cargo test` to // re-generate that file. @@ -111,6 +119,9 @@ config_data! { /// Whether to show `Run` action. Only applies when /// `#rust-analyzer.hover.actions.enable#` is set. hover_actions_run_enable: bool = true, + /// Whether to show `Update Test` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. + hover_actions_updateTest_enable: bool = true, /// Whether to show documentation on hover. hover_documentation_enable: bool = true, @@ -119,6 +130,12 @@ config_data! { hover_documentation_keywords_enable: bool = true, /// Use markdown syntax for links on hover. hover_links_enable: bool = true, + /// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + /// + /// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + /// + /// The default is 20 characters. + hover_maxSubstitutionLength: Option<MaxSubstitutionLength> = Some(MaxSubstitutionLength::Limit(20)), /// How to render the align information in a memory layout hover. hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), /// Whether to show memory layout data on hover. @@ -229,6 +246,9 @@ config_data! { /// Whether to show `Run` lens. Only applies when /// `#rust-analyzer.lens.enable#` is set. lens_run_enable: bool = true, + /// Whether to show `Update Test` lens. Only applies when + /// `#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + lens_updateTest_enable: bool = true, /// Disable project auto-discovery in favor of explicitly specified set /// of projects. @@ -426,11 +446,32 @@ config_data! { /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, + /// A list of full paths to items to exclude from auto-importing completions. + /// + /// Traits in this list won't have their methods suggested in completions unless the trait + /// is in scope. + /// + /// You can either specify a string path which defaults to type "always" or use the more verbose + /// form `{ "path": "path::to::item", type: "always" }`. + /// + /// For traits the type "methods" can be used to only exclude the methods but not the trait itself. + /// + /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + completion_autoimport_exclude: Vec<AutoImportExclusion> = vec![ + AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, + AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods }, + ], /// Toggles the additional completions that automatically show method calls and field accesses /// with `self` prefixed to them when inside a method. completion_autoself_enable: bool = true, /// Whether to add parenthesis and argument snippets when completing function. completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// A list of full paths to traits whose methods to exclude from completion. + /// + /// Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + /// + /// Note that the trait themselves can still be completed. + completion_excludeTraits: Vec<String> = Vec::new(), /// Whether to show full function/method signatures in completion docs. completion_fullFunctionSignatures_enable: bool = false, /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. @@ -554,15 +595,12 @@ config_data! { /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option<String> = Some("discover".to_owned()), - /// How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer - /// to analyze third-party dependencies of the standard libraries. - cargo_sysrootQueryMetadata: SysrootQueryMetadata = SysrootQueryMetadata::CargoMetadata, /// Relative path to the sysroot library sources. If left unset, this will default to /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysrootSrc: Option<String> = None, - /// Compilation target override (target triple). + /// Compilation target override (target tuple). // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work // than `checkOnSave_target` cargo_target: Option<String> = None, @@ -854,7 +892,7 @@ impl Config { if let Some(mut json) = change.client_config_change { tracing::info!("updating config from JSON: {:#}", json); - if !(json.is_null() || json.as_object().map_or(false, |it| it.is_empty())) { + if !(json.is_null() || json.as_object().is_some_and(|it| it.is_empty())) { let mut json_errors = vec![]; let detached_files = get_field_json::<Vec<Utf8PathBuf>>( &mut json, @@ -869,11 +907,18 @@ impl Config { patch_old_style::patch_json_for_outdated_configs(&mut json); + let mut json_errors = vec![]; + let snips = get_field_json::<FxHashMap<String, SnippetDef>>( + &mut json, + &mut json_errors, + "completion_snippets_custom", + None, + ) + .unwrap_or(self.completion_snippets_custom().to_owned()); + // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`. config.snippets.clear(); - let snips = self.completion_snippets_custom().to_owned(); - for (name, def) in snips.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { continue; @@ -1147,6 +1192,7 @@ pub struct LensConfig { // runnables pub run: bool, pub debug: bool, + pub update_test: bool, pub interpret: bool, // implementations @@ -1182,6 +1228,7 @@ impl LensConfig { pub fn any(&self) -> bool { self.run || self.debug + || self.update_test || self.implementations || self.method_refs || self.refs_adt @@ -1194,7 +1241,7 @@ impl LensConfig { } pub fn runnable(&self) -> bool { - self.run || self.debug + self.run || self.debug || self.update_test } pub fn references(&self) -> bool { @@ -1208,6 +1255,7 @@ pub struct HoverActionsConfig { pub references: bool, pub run: bool, pub debug: bool, + pub update_test: bool, pub goto_type_def: bool, } @@ -1217,6 +1265,7 @@ impl HoverActionsConfig { references: false, run: false, debug: false, + update_test: false, goto_type_def: false, }; @@ -1229,7 +1278,7 @@ impl HoverActionsConfig { } pub fn runnable(&self) -> bool { - self.run || self.debug + self.run || self.debug || self.update_test } } @@ -1417,7 +1466,7 @@ impl Config { CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() } } - pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig { + pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig<'_> { let client_capability_fields = self.completion_resolve_support_properties(); CompletionConfig { enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), @@ -1448,6 +1497,27 @@ impl Config { } else { CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields) }, + exclude_flyimport: self + .completion_autoimport_exclude(source_root) + .iter() + .map(|it| match it { + AutoImportExclusion::Path(path) => { + (path.clone(), ide_completion::AutoImportExclusionType::Always) + } + AutoImportExclusion::Verbose { path, r#type } => ( + path.clone(), + match r#type { + AutoImportExclusionType::Always => { + ide_completion::AutoImportExclusionType::Always + } + AutoImportExclusionType::Methods => { + ide_completion::AutoImportExclusionType::Methods + } + }, + ), + }) + .collect(), + exclude_traits: self.completion_excludeTraits(source_root), } } @@ -1503,6 +1573,9 @@ impl Config { references: enable && self.hover_actions_references_enable().to_owned(), run: enable && self.hover_actions_run_enable().to_owned(), debug: enable && self.hover_actions_debug_enable().to_owned(), + update_test: enable + && self.hover_actions_run_enable().to_owned() + && self.hover_actions_updateTest_enable().to_owned(), goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(), } } @@ -1533,6 +1606,11 @@ impl Config { max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), max_fields_count: self.hover_show_fields().to_owned(), max_enum_variants_count: self.hover_show_enumVariants().to_owned(), + max_subst_ty_len: match self.hover_maxSubstitutionLength() { + Some(MaxSubstitutionLength::Hide) => ide::SubstTyLen::Hide, + Some(MaxSubstitutionLength::Limit(limit)) => ide::SubstTyLen::LimitTo(*limit), + None => ide::SubstTyLen::Unlimited, + }, } } @@ -1860,12 +1938,6 @@ impl Config { }, target: self.cargo_target(source_root).clone(), sysroot, - sysroot_query_metadata: match self.cargo_sysrootQueryMetadata(None) { - SysrootQueryMetadata::CargoMetadata => { - project_model::SysrootQueryMetadata::CargoMetadata - } - SysrootQueryMetadata::None => project_model::SysrootQueryMetadata::None, - }, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { @@ -1969,7 +2041,7 @@ impl Config { pub(crate) fn cargo_test_options(&self, source_root: Option<SourceRootId>) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target(source_root).clone().into_iter().collect(), + target_tuples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, no_default_features: *self.cargo_noDefaultFeatures(source_root), all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All), @@ -2004,7 +2076,7 @@ impl Config { Some(_) | None => FlycheckConfig::CargoCommand { command: self.check_command(source_root).clone(), options: CargoOptions { - target_triples: self + target_tuples: self .check_targets(source_root) .clone() .and_then(|targets| match &targets.0[..] { @@ -2047,11 +2119,13 @@ impl Config { fn target_dir_from_config(&self, source_root: Option<SourceRootId>) -> Option<Utf8PathBuf> { self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { - Some(Utf8PathBuf::from("target/rust-analyzer")) + let env_var = env::var("CARGO_TARGET_DIR").ok(); + let mut path = Utf8PathBuf::from(env_var.as_deref().unwrap_or("target")); + path.push("rust-analyzer"); + Some(path) } TargetDirectory::UseSubdirectory(false) => None, - TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()), - TargetDirectory::Directory(_) => None, + TargetDirectory::Directory(dir) => Some(dir.clone()), }) } @@ -2096,6 +2170,9 @@ impl Config { LensConfig { run: *self.lens_enable() && *self.lens_run_enable(), debug: *self.lens_enable() && *self.lens_debug_enable(), + update_test: *self.lens_enable() + && *self.lens_updateTest_enable() + && *self.lens_run_enable(), interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), implementations: *self.lens_enable() && *self.lens_implementations_enable(), method_refs: *self.lens_enable() && *self.lens_references_method_enable(), @@ -2378,6 +2455,21 @@ enum ExprFillDefaultDef { } #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +#[serde(rename_all = "snake_case")] +pub enum AutoImportExclusion { + Path(String), + Verbose { path: String, r#type: AutoImportExclusionType }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub enum AutoImportExclusionType { + Always, + Methods, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ImportGranularityDef { Preserve, @@ -2568,13 +2660,6 @@ pub enum NumThreads { Concrete(usize), } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum SysrootQueryMetadata { - CargoMetadata, - None, -} - macro_rules! _default_val { (@verbatim: $s:literal, $ty:ty) => {{ let default_: $ty = serde_json::from_str(&$s).unwrap(); @@ -3426,13 +3511,45 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json } ] }, - "SysrootQueryMetadata" => set! { - "type": "string", - "enum": ["none", "cargo_metadata"], - "enumDescriptions": [ - "Do not query sysroot metadata, always use stitched sysroot.", - "Use `cargo metadata` to query sysroot metadata." - ], + "Option<MaxSubstitutionLength>" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": ["hide"] + }, + { + "type": "integer" + } + ] + }, + "Vec<AutoImportExclusion>" => set! { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + }, + { + "type": "object", + "properties": { + "path": { + "type": "string", + }, + "type": { + "type": "string", + "enum": ["always", "methods"], + "enumDescriptions": [ + "Do not show this item or its methods (if it is a trait) in auto-import completions.", + "Do not show this traits methods in auto-import completions." + ], + }, + } + } + ] + } }, _ => panic!("missing entry for {ty}: {default} (field {field})"), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index c3ab7f3ae71..fafffa043f9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -500,7 +500,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> { code.filter(|code| { let mut chars = code.chars(); - chars.next().map_or(false, |c| c == 'E') + chars.next() == Some('E') && chars.by_ref().take(4).all(|c| c.is_ascii_digit()) && chars.next().is_none() }) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 96b164228ef..0c111319bb4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -1,6 +1,6 @@ //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery //! via a custom discover command. -use std::{io, process::Command}; +use std::{io, path::Path}; use crossbeam_channel::Sender; use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf}; @@ -43,7 +43,11 @@ impl DiscoverCommand { } /// Spawn the command inside [Discover] and report progress, if any. - pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result<DiscoverHandle> { + pub(crate) fn spawn( + &self, + discover_arg: DiscoverArgument, + current_dir: &Path, + ) -> io::Result<DiscoverHandle> { let command = &self.command[0]; let args = &self.command[1..]; @@ -58,7 +62,7 @@ impl DiscoverCommand { }) .collect(); - let mut cmd = Command::new(command); + let mut cmd = toolchain::command(command, current_dir); cmd.args(args); Ok(DiscoverHandle { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 53c145f884e..22f06d68d80 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1,10 +1,11 @@ //! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -use std::{fmt, io, mem, process::Command, time::Duration}; +use std::{fmt, io, process::Command, time::Duration}; use cargo_metadata::PackageId; use crossbeam_channel::{select_biased, unbounded, Receiver, Sender}; +use ide_db::FxHashSet; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize as _; @@ -27,7 +28,7 @@ pub(crate) enum InvocationStrategy { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { - pub(crate) target_triples: Vec<String>, + pub(crate) target_tuples: Vec<String>, pub(crate) all_targets: bool, pub(crate) no_default_features: bool, pub(crate) all_features: bool, @@ -38,7 +39,7 @@ pub(crate) struct CargoOptions { pub(crate) target_dir: Option<Utf8PathBuf>, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) enum Target { Bin(String), Example(String), @@ -48,7 +49,7 @@ pub(crate) enum Target { impl CargoOptions { pub(crate) fn apply_on_command(&self, cmd: &mut Command) { - for target in &self.target_triples { + for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } if self.all_targets { @@ -231,13 +232,9 @@ struct FlycheckActor { command_handle: Option<CommandHandle<CargoCheckMessage>>, /// The receiver side of the channel mentioned above. command_receiver: Option<Receiver<CargoCheckMessage>>, - package_status: FxHashMap<Arc<PackageId>, DiagnosticReceived>, -} - -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -enum DiagnosticReceived { - Yes, - No, + diagnostics_cleared_for: FxHashSet<Arc<PackageId>>, + diagnostics_cleared_for_all: bool, + diagnostics_received: bool, } #[allow(clippy::large_enum_variant)] @@ -267,7 +264,9 @@ impl FlycheckActor { manifest_path, command_handle: None, command_receiver: None, - package_status: FxHashMap::default(), + diagnostics_cleared_for: Default::default(), + diagnostics_cleared_for_all: false, + diagnostics_received: false, } } @@ -344,23 +343,16 @@ impl FlycheckActor { error ); } - if self.package_status.is_empty() { + if !self.diagnostics_received { + tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. - // That means all of them are stale. + // Clear everything for good measure self.send(FlycheckMessage::ClearDiagnostics { id: self.id, package_id: None, }); - } else { - for (package_id, status) in mem::take(&mut self.package_status) { - if let DiagnosticReceived::No = status { - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - package_id: Some(package_id), - }); - } - } } + self.clear_diagnostics_state(); self.report_progress(Progress::DidFinish(res)); } @@ -373,9 +365,18 @@ impl FlycheckActor { "artifact received" ); self.report_progress(Progress::DidCheckCrate(msg.target.name)); - self.package_status - .entry(Arc::new(msg.package_id)) - .or_insert(DiagnosticReceived::No); + let package_id = Arc::new(msg.package_id); + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.repr, + "clearing diagnostics" + ); + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + package_id: Some(package_id), + }); + } } CargoCheckMessage::Diagnostic { diagnostic, package_id } => { tracing::trace!( @@ -384,15 +385,25 @@ impl FlycheckActor { package_id = package_id.as_ref().map(|it| &it.repr), "diagnostic received" ); + self.diagnostics_received = true; if let Some(package_id) = &package_id { - if !self.package_status.contains_key(package_id) { - self.package_status - .insert(package_id.clone(), DiagnosticReceived::Yes); + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.repr, + "clearing diagnostics" + ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, package_id: Some(package_id.clone()), }); } + } else if !self.diagnostics_cleared_for_all { + self.diagnostics_cleared_for_all = true; + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + package_id: None, + }); } self.send(FlycheckMessage::AddDiagnostic { id: self.id, @@ -417,8 +428,14 @@ impl FlycheckActor { command_handle.cancel(); self.command_receiver.take(); self.report_progress(Progress::DidCancel); - self.package_status.clear(); } + self.clear_diagnostics_state(); + } + + fn clear_diagnostics_state(&mut self) { + self.diagnostics_cleared_for.clear(); + self.diagnostics_cleared_for_all = false; + self.diagnostics_received = false; } /// Construct a `Command` object for checking the user's code. If the user @@ -432,12 +449,11 @@ impl FlycheckActor { ) -> Option<Command> { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root); if let Some(sysroot_root) = &self.sysroot_root { cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root)); } cmd.arg(command); - cmd.current_dir(&*self.root); match package { Some(pkg) => cmd.arg("-p").arg(pkg), @@ -462,7 +478,7 @@ impl FlycheckActor { if let Some(manifest_path) = &self.manifest_path { cmd.arg("--manifest-path"); cmd.arg(manifest_path); - if manifest_path.extension().map_or(false, |ext| ext == "rs") { + if manifest_path.extension() == Some("rs") { cmd.arg("-Zscript"); } } @@ -474,18 +490,15 @@ impl FlycheckActor { Some(cmd) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { - let mut cmd = Command::new(command); - cmd.envs(extra_env); - - match invocation_strategy { - InvocationStrategy::Once => { - cmd.current_dir(&*self.root); - } + let root = match invocation_strategy { + InvocationStrategy::Once => &*self.root, InvocationStrategy::PerWorkspace => { - // FIXME: cmd.current_dir(&affected_workspace); - cmd.current_dir(&*self.root); + // FIXME: &affected_workspace + &*self.root } - } + }; + let mut cmd = toolchain::command(command, root); + cmd.envs(extra_env); // If the custom command has a $saved_file placeholder, and // we're saving a file, replace the placeholder in the arguments. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index dd13bdba4cb..0f2d7823b7e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -17,7 +17,7 @@ use parking_lot::{ MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; -use proc_macro_api::ProcMacroServer; +use proc_macro_api::ProcMacroClient; use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::{span, trace, Level}; @@ -95,7 +95,7 @@ pub(crate) struct GlobalState { pub(crate) last_reported_status: lsp_ext::ServerStatusParams, // proc macros - pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>, + pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroClient>]>, pub(crate) build_deps_changed: bool, // Flycheck @@ -726,7 +726,6 @@ impl GlobalStateSnapshot { }; return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec { - crate_id, label: build.label, target_kind: build.target_kind, shell_runnables: project.runnables().to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 2aa4ffbe1dc..ff50f7533a6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -5,6 +5,7 @@ use std::{ }; use ide::Cancelled; +use ide_db::base_db::ra_salsa::Cycle; use lsp_server::{ExtractError, Response, ResponseError}; use serde::{de::DeserializeOwned, Serialize}; use stdx::thread::ThreadIntent; @@ -307,10 +308,31 @@ impl RequestDispatcher<'_> { } } +#[derive(Debug)] +enum HandlerCancelledError { + PropagatedPanic, + Inner(ide::Cancelled), +} + +impl std::error::Error for HandlerCancelledError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + HandlerCancelledError::PropagatedPanic => None, + HandlerCancelledError::Inner(cancelled) => Some(cancelled), + } + } +} + +impl fmt::Display for HandlerCancelledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Cancelled") + } +} + fn thread_result_to_response<R>( id: lsp_server::RequestId, result: thread::Result<anyhow::Result<R::Result>>, -) -> Result<lsp_server::Response, Cancelled> +) -> Result<lsp_server::Response, HandlerCancelledError> where R: lsp_types::request::Request, R::Params: DeserializeOwned, @@ -328,7 +350,13 @@ where if let Some(panic_message) = panic_message { message.push_str(": "); message.push_str(panic_message) - }; + } else if let Some(cycle) = panic.downcast_ref::<Cycle>() { + tracing::error!("Cycle propagated out of salsa! This is a bug: {cycle:?}"); + return Err(HandlerCancelledError::PropagatedPanic); + } else if let Ok(cancelled) = panic.downcast::<Cancelled>() { + tracing::error!("Cancellation propagated out of salsa! This is a bug"); + return Err(HandlerCancelledError::Inner(*cancelled)); + } Ok(lsp_server::Response::new_err( id, @@ -342,7 +370,7 @@ where fn result_to_response<R>( id: lsp_server::RequestId, result: anyhow::Result<R::Result>, -) -> Result<lsp_server::Response, Cancelled> +) -> Result<lsp_server::Response, HandlerCancelledError> where R: lsp_types::request::Request, R::Params: DeserializeOwned, @@ -353,7 +381,7 @@ where Err(e) => match e.downcast::<LspError>() { Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), Err(e) => match e.downcast::<Cancelled>() { - Ok(cancelled) => return Err(cancelled), + Ok(cancelled) => return Err(HandlerCancelledError::Inner(cancelled)), Err(e) => lsp_server::Response::new_err( id, lsp_server::ErrorCode::InternalError as i32, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index c0231fd04e5..98efc637c2c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -10,7 +10,6 @@ use lsp_types::{ DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams, }; use paths::Utf8PathBuf; -use stdx::TupleExt; use triomphe::Arc; use vfs::{AbsPathBuf, ChangeKind, VfsPath}; @@ -75,7 +74,6 @@ pub(crate) fn handle_did_open_text_document( tracing::error!("duplicate DidOpenTextDocument: {}", path); } - tracing::info!("New file content set {:?}", params.text_document.text); state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); if state.config.discover_workspace_config().is_some() { tracing::debug!("queuing task"); @@ -296,12 +294,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { - // Is the target binary? If so we let flycheck run only for the workspace that contains the crate. let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); - let (tgt_name, crate_id) = match it { - TargetSpec::Cargo(c) => (c.target, c.crate_id), - TargetSpec::ProjectJson(p) => (p.label, p.crate_id), + let (tgt_name, root, package) = match it { + TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), + _ => return None, }; let tgt = match tgt_kind { @@ -309,28 +306,50 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { project_model::TargetKind::Example => Target::Example(tgt_name), project_model::TargetKind::Test => Target::Test(tgt_name), project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return None, + _ => return Some((None, root, package)), }; - Some((tgt, crate_id)) + Some((Some(tgt), root, package)) }); - - let crate_ids = match target { - // Trigger flychecks for the only crate which the target belongs to - Some((_, krate)) => vec![krate], - None => { - // Trigger flychecks for all workspaces that depend on the saved file - // Crates containing or depending on the saved file - world - .analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| world.analysis.transitive_rev_deps(id)) - .flatten() - .unique() - .collect::<Vec<_>>() + tracing::debug!(?target, "flycheck target"); + // we have a specific non-library target, attempt to only check that target, nothing + // else will be affected + if let Some((target, root, package)) = target { + // trigger a package check if we have a non-library target as that can't affect + // anything else in the workspace OR if we're not allowed to check the workspace as + // the user opted into package checks then + let package_check_allowed = target.is_some() || !may_flycheck_workspace; + if package_check_allowed { + let workspace = + world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some((idx, _)) = workspace { + world.flycheck[idx].restart_for_package(package, target); + } } - }; + } + + if !may_flycheck_workspace { + return Ok(()); + } + + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .unique() + .collect::<Vec<_>>(); + tracing::debug!(?crate_ids, "flycheck crate ids"); let crate_root_paths: Vec<_> = crate_ids .iter() .filter_map(|&crate_id| { @@ -344,53 +363,41 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { }) .collect::<ide::Cancellable<_>>()?; let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect(); + tracing::debug!(?crate_root_paths, "flycheck crate roots"); // Find all workspaces that have at least one target containing the saved file - let workspace_ids = world.workspaces.iter().enumerate().filter_map(|(idx, ws)| { - let package = match &ws.kind { + let workspace_ids = + world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind { project_model::ProjectWorkspaceKind::Cargo { cargo, .. } | project_model::ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. - } => cargo.packages().find_map(|pkg| { - let has_target_with_root = cargo[pkg] + } => cargo.packages().any(|pkg| { + cargo[pkg] .targets .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo.package_flag(&cargo[pkg])) + .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())) }), - project_model::ProjectWorkspaceKind::Json(project) => { - if !project.crates().any(|(_, krate)| { - crate_root_paths.contains(&krate.root_module.as_path()) - }) { - return None; - } - None - } - project_model::ProjectWorkspaceKind::DetachedFile { .. } => return None, - }; - Some((idx, package)) - }); + project_model::ProjectWorkspaceKind::Json(project) => project + .crates() + .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())), + project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, + }); let saved_file = vfs_path.as_path().map(|p| p.to_owned()); // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { - for (id, package) in workspace_ids.clone() { + for (id, _) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - if may_flycheck_workspace { - flycheck.restart_workspace(saved_file.clone()) - } else if let Some(package) = package { - flycheck - .restart_for_package(package, target.clone().map(TupleExt::head)) - } + flycheck.restart_workspace(saved_file.clone()); continue 'flychecks; } } } // No specific flycheck was triggered, so let's trigger all of them. - if !updated && may_flycheck_workspace { + if !updated { for flycheck in world.flycheck.iter() { flycheck.restart_workspace(saved_file.clone()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 8f2bf80ea26..7ac70efe2d6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1,12 +1,7 @@ //! This module is responsible for implementing handlers for Language Server //! Protocol. This module specifically handles requests. -use std::{ - fs, - io::Write as _, - ops::Not, - process::{self, Stdio}, -}; +use std::{fs, io::Write as _, ops::Not, process::Stdio}; use anyhow::Context; @@ -32,7 +27,7 @@ use paths::Utf8PathBuf; use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind}; use serde_json::json; use stdx::{format_to, never}; -use syntax::{algo, ast, AstNode, TextRange, TextSize}; +use syntax::{TextRange, TextSize}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; @@ -933,39 +928,32 @@ pub(crate) fn handle_runnables( let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok()); let target_spec = TargetSpec::for_file(&snap, file_id)?; - let expect_test = match offset { - Some(offset) => { - let source_file = snap.analysis.parse(file_id)?; - algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) - .and_then(|it| it.path()?.segment()?.name_ref()) - .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file") - } - None => false, - }; - let mut res = Vec::new(); for runnable in snap.analysis.runnables(file_id)? { - if should_skip_for_offset(&runnable, offset) { - continue; - } - if should_skip_target(&runnable, target_spec.as_ref()) { + if should_skip_for_offset(&runnable, offset) + || should_skip_target(&runnable, target_spec.as_ref()) + { continue; } + + let update_test = runnable.update_test; if let Some(mut runnable) = to_proto::runnable(&snap, runnable)? { - if expect_test { - if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { - runnable.label = format!("{} + expect", runnable.label); - r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned()); - if let Some(TargetSpec::Cargo(CargoTargetSpec { - sysroot_root: Some(sysroot_root), - .. - })) = &target_spec - { - r.environment - .insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); - } - } + if let Some(runnable) = + to_proto::make_update_runnable(&runnable, &update_test.label(), &update_test.env()) + { + res.push(runnable); } + + if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { + if let Some(TargetSpec::Cargo(CargoTargetSpec { + sysroot_root: Some(sysroot_root), + .. + })) = &target_spec + { + r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); + } + }; + res.push(runnable); } } @@ -1831,6 +1819,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( let doc = TextDocumentIdentifier::new(item.uri); let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; + let line_index = snap.file_line_index(fpos.file_id)?; let config = snap.config.call_hierarchy(); let call_items = match snap.analysis.outgoing_calls(config, fpos)? { @@ -1841,8 +1830,6 @@ pub(crate) fn handle_call_hierarchy_outgoing( let mut res = vec![]; for call_item in call_items.into_iter() { - let file_id = call_item.target.file_id; - let line_index = snap.file_line_index(file_id)?; let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; res.push(CallHierarchyOutgoingCall { to: item, @@ -2148,6 +2135,7 @@ fn runnable_action_links( } let title = runnable.title(); + let update_test = runnable.update_test; let r = to_proto::runnable(snap, runnable).ok()??; let mut group = lsp_ext::CommandLinkGroup::default(); @@ -2159,7 +2147,15 @@ fn runnable_action_links( if hover_actions_config.debug && client_commands_config.debug_single { let dbg_command = to_proto::command::debug_single(&r); - group.commands.push(to_command_link(dbg_command, r.label)); + group.commands.push(to_command_link(dbg_command, r.label.clone())); + } + + if hover_actions_config.update_test && client_commands_config.run_single { + let label = update_test.label(); + if let Some(r) = to_proto::make_update_runnable(&r, &label, &update_test.env()) { + let update_command = to_proto::command::run_single(&r, label.unwrap().as_str()); + group.commands.push(to_command_link(update_command, r.label.clone())); + } } Some(group) @@ -2243,10 +2239,31 @@ fn run_rustfmt( let line_index = snap.file_line_index(file_id)?; let source_root_id = snap.analysis.source_root_id(file_id).ok(); + // try to chdir to the file so we can respect `rustfmt.toml` + // FIXME: use `rustfmt --config-path` once + // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed + let current_dir = match text_document.uri.to_file_path() { + Ok(mut path) => { + // pop off file name + if path.pop() && path.is_dir() { + path + } else { + std::env::current_dir()? + } + } + Err(_) => { + tracing::error!( + text_document = ?text_document.uri, + "Unable to get path, rustfmt.toml might be ignored" + ); + std::env::current_dir()? + } + }; + let mut command = match snap.config.rustfmt(source_root_id) { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN - let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); + let mut cmd = toolchain::command(toolchain::Tool::Rustfmt.path(), current_dir); cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); @@ -2300,9 +2317,9 @@ fn run_rustfmt( } else { cmd }; - process::Command::new(cmd_path) + toolchain::command(cmd_path, current_dir) } - _ => process::Command::new(cmd), + _ => toolchain::command(cmd, current_dir), }; cmd.envs(snap.config.extra_env(source_root_id)); @@ -2313,24 +2330,6 @@ fn run_rustfmt( tracing::debug!(?command, "created format command"); - // try to chdir to the file so we can respect `rustfmt.toml` - // FIXME: use `rustfmt --config-path` once - // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed - match text_document.uri.to_file_path() { - Ok(mut path) => { - // pop off file name - if path.pop() && path.is_dir() { - command.current_dir(path); - } - } - Err(_) => { - tracing::error!( - text_document = ?text_document.uri, - "Unable to get path, rustfmt.toml might be ignored" - ); - } - } - let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 8946c7acb93..fcfd06679bf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -174,6 +174,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -222,6 +224,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -268,6 +272,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index c0173d9c247..f50cbba7acf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -1,5 +1,9 @@ //! rust-analyzer extensions to the LSP. +// Note when adding new resolve payloads, add a #[serde(default)] on boolean fields as some clients +// might strip `false` values from the JSON payload due to their reserialization logic turning false +// into null which will then cause them to be omitted in the resolve request. See https://github.com/rust-lang/rust-analyzer/issues/18767 + #![allow(clippy::disallowed_types)] use std::ops; @@ -427,14 +431,14 @@ impl Request for Runnables { const METHOD: &'static str = "experimental/runnables"; } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct RunnablesParams { pub text_document: TextDocumentIdentifier, pub position: Option<Position>, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Runnable { pub label: String, @@ -444,7 +448,7 @@ pub struct Runnable { pub args: RunnableArgs, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] #[serde(untagged)] pub enum RunnableArgs { @@ -452,14 +456,14 @@ pub enum RunnableArgs { Shell(ShellRunnableArgs), } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "lowercase")] pub enum RunnableKind { Cargo, Shell, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CargoRunnableArgs { #[serde(skip_serializing_if = "FxHashMap::is_empty")] @@ -475,7 +479,7 @@ pub struct CargoRunnableArgs { pub executable_args: Vec<String>, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ShellRunnableArgs { #[serde(skip_serializing_if = "FxHashMap::is_empty")] @@ -829,6 +833,7 @@ pub struct CompletionResolveData { pub version: Option<i32>, #[serde(skip_serializing_if = "Option::is_none", default)] pub trigger_character: Option<char>, + #[serde(default)] pub for_ref: bool, pub hash: String, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 05e93b4e6ac..fe4d02dcb4f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -20,6 +20,7 @@ use itertools::Itertools; use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; use serde_json::to_value; +use syntax::SmolStr; use vfs::AbsPath; use crate::{ @@ -560,8 +561,7 @@ pub(crate) fn inlay_hint( let text_edits = if snap .config .visual_studio_code_version() - // https://github.com/microsoft/vscode/issues/193124 - .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) + .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) && resolve_range_and_hash.is_some() && fields_to_resolve.resolve_text_edits { @@ -1567,6 +1567,7 @@ pub(crate) fn code_lens( let line_index = snap.file_line_index(run.nav.file_id)?; let annotation_range = range(&line_index, annotation.range); + let update_test = run.update_test; let title = run.title(); let can_debug = match run.kind { ide::RunnableKind::DocTest { .. } => false, @@ -1602,6 +1603,18 @@ pub(crate) fn code_lens( data: None, }) } + if lens_config.update_test && client_commands_config.run_single { + let label = update_test.label(); + let env = update_test.env(); + if let Some(r) = make_update_runnable(&r, &label, &env) { + let command = command::run_single(&r, label.unwrap().as_str()); + acc.push(lsp_types::CodeLens { + range: annotation_range, + command: Some(command), + data: None, + }) + } + } } if lens_config.interpret { @@ -1786,7 +1799,7 @@ pub(crate) mod command { pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command { lsp_types::Command { - title: "Debug".into(), + title: "âš™\u{fe0e} Debug".into(), command: "rust-analyzer.debugSingle".into(), arguments: Some(vec![to_value(runnable).unwrap()]), } @@ -1838,6 +1851,28 @@ pub(crate) mod command { } } +pub(crate) fn make_update_runnable( + runnable: &lsp_ext::Runnable, + label: &Option<SmolStr>, + env: &[(&str, &str)], +) -> Option<lsp_ext::Runnable> { + if !matches!(runnable.args, lsp_ext::RunnableArgs::Cargo(_)) { + return None; + } + let label = label.as_ref()?; + + let mut runnable = runnable.clone(); + runnable.label = format!("{} + {}", runnable.label, label); + + let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args else { + unreachable!(); + }; + + r.environment.extend(env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); + + Some(runnable) +} + pub(crate) fn implementation_title(count: usize) -> String { if count == 1 { "1 implementation".into() diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index d97d96d54a0..97657b92658 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -744,7 +744,8 @@ impl GlobalState { DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = discover.spawn(arg).unwrap(); + let handle = + discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap(); self.discover_handle = Some(handle); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 3444773695b..0add2cdf5a7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -24,7 +24,7 @@ use ide_db::{ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use lsp_types::FileSystemWatcher; -use proc_macro_api::ProcMacroServer; +use proc_macro_api::ProcMacroClient; use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; @@ -630,13 +630,10 @@ impl GlobalState { }; let env: FxHashMap<_, _> = match &ws.kind { - ProjectWorkspaceKind::Cargo { cargo_config_extra_env, .. } - | ProjectWorkspaceKind::DetachedFile { - cargo: Some(_), - cargo_config_extra_env, - .. - } => cargo_config_extra_env - .iter() + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, ..)), .. } => cargo + .env() + .into_iter() .chain(self.config.extra_env(None)) .map(|(a, b)| (a.clone(), b.clone())) .chain( @@ -650,7 +647,7 @@ impl GlobalState { }; info!("Using proc-macro server at {path}"); - ProcMacroServer::spawn(&path, &env).map_err(|err| { + ProcMacroClient::spawn(&path, &env).map_err(|err| { tracing::error!( "Failed to run proc-macro server from path {path}, error: {err:?}", ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index b4aa73d2780..b28567fe09b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -62,7 +62,6 @@ pub(crate) struct CargoTargetSpec { #[derive(Clone, Debug)] pub(crate) struct ProjectJsonTargetSpec { - pub(crate) crate_id: CrateId, pub(crate) label: String, pub(crate) target_kind: TargetKind, pub(crate) shell_runnables: Vec<Runnable>, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 2fd52547336..503b3ee43a1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -1,8 +1,6 @@ //! This module provides the functionality needed to run `cargo test` in a background //! thread and report the result of each test in a channel. -use std::process::Command; - use crossbeam_channel::Sender; use paths::AbsPath; use serde::Deserialize as _; @@ -78,7 +76,7 @@ impl CargoTestHandle { test_target: TestTarget, sender: Sender<CargoTestMessage>, ) -> std::io::Result<Self> { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), root); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index c1ca5960637..2b3c0a47a22 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1082,11 +1082,11 @@ fn resolve_proc_macro() { return; } - let sysroot = project_model::Sysroot::discover( + let mut sysroot = project_model::Sysroot::discover( &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), - project_model::SysrootQueryMetadata::CargoMetadata, ); + sysroot.load_workspace(&project_model::SysrootSourceWorkspaceConfig::default_cargo()); let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap(); diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index 20c3b087af5..8dc95735038 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -358,6 +358,18 @@ impl HirFileId { } } +/// Legacy span type, only defined here as it is still used by the proc-macro server. +/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for +/// proc-macro expansion. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct TokenId(pub u32); + +impl std::fmt::Debug for TokenId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + #[cfg(not(feature = "ra-salsa"))] mod intern_id_proxy { use std::fmt; diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 3a05b83e497..ed8b1908d60 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -5,17 +5,14 @@ use std::fmt; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, SpanAnchor, SpanData, SpanMap}; -use stdx::{format_to, never, non_empty_vec::NonEmptyVec}; +use stdx::{format_to, never}; use syntax::{ ast::{self, make::tokens::doc_comment}, format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{ - buffer::{Cursor, TokenBuffer}, - token_to_literal, -}; +use tt::{buffer::Cursor, token_to_literal}; pub mod prettify_macro_expansion; mod to_parser_input; @@ -99,7 +96,7 @@ pub fn syntax_node_to_token_tree<Ctx, SpanMap>( map: SpanMap, span: SpanData<Ctx>, mode: DocCommentDesugarMode, -) -> tt::Subtree<SpanData<Ctx>> +) -> tt::TopSubtree<SpanData<Ctx>> where SpanData<Ctx>: Copy + fmt::Debug, SpanMap: SpanMapper<SpanData<Ctx>>, @@ -118,7 +115,7 @@ pub fn syntax_node_to_token_tree_modified<Ctx, SpanMap>( remove: FxHashSet<SyntaxElement>, call_site: SpanData<Ctx>, mode: DocCommentDesugarMode, -) -> tt::Subtree<SpanData<Ctx>> +) -> tt::TopSubtree<SpanData<Ctx>> where SpanMap: SpanMapper<SpanData<Ctx>>, SpanData<Ctx>: Copy + fmt::Debug, @@ -142,7 +139,7 @@ where /// Converts a [`tt::Subtree`] back to a [`SyntaxNode`]. /// The produced `SpanMap` contains a mapping from the syntax nodes offsets to the subtree's spans. pub fn token_tree_to_syntax_node<Ctx>( - tt: &tt::Subtree<SpanData<Ctx>>, + tt: &tt::TopSubtree<SpanData<Ctx>>, entry_point: parser::TopEntryPoint, edition: parser::Edition, ) -> (Parse<SyntaxNode>, SpanMap<Ctx>) @@ -150,16 +147,10 @@ where SpanData<Ctx>: Copy + fmt::Debug, Ctx: PartialEq, { - let buffer = match tt { - tt::Subtree { - delimiter: tt::Delimiter { kind: tt::DelimiterKind::Invisible, .. }, - token_trees, - } => TokenBuffer::from_tokens(token_trees), - _ => TokenBuffer::from_subtree(tt), - }; - let parser_input = to_parser_input(edition, &buffer); + let buffer = tt.view().strip_invisible(); + let parser_input = to_parser_input(edition, buffer); let parser_output = entry_point.parse(&parser_input, edition); - let mut tree_sink = TtTreeSink::new(buffer.begin()); + let mut tree_sink = TtTreeSink::new(buffer.cursor()); for event in parser_output.iter() { match event { parser::Step::Token { kind, n_input_tokens: n_raw_tokens } => { @@ -183,7 +174,7 @@ pub fn parse_to_token_tree<Ctx>( anchor: SpanAnchor, ctx: Ctx, text: &str, -) -> Option<tt::Subtree<SpanData<Ctx>>> +) -> Option<tt::TopSubtree<SpanData<Ctx>>> where SpanData<Ctx>: Copy + fmt::Debug, Ctx: Copy, @@ -202,7 +193,7 @@ pub fn parse_to_token_tree_static_span<S>( edition: Edition, span: S, text: &str, -) -> Option<tt::Subtree<S>> +) -> Option<tt::TopSubtree<S>> where S: Copy + fmt::Debug, { @@ -215,47 +206,38 @@ where Some(convert_tokens(&mut conv)) } -fn convert_tokens<S, C>(conv: &mut C) -> tt::Subtree<S> +fn convert_tokens<S, C>(conv: &mut C) -> tt::TopSubtree<S> where C: TokenConverter<S>, S: Copy + fmt::Debug, C::Token: fmt::Debug, { - let entry = tt::SubtreeBuilder { - delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), - token_trees: vec![], - }; - let mut stack = NonEmptyVec::new(entry); + let mut builder = + tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site())); while let Some((token, abs_range)) = conv.bump() { - let tt::SubtreeBuilder { delimiter, token_trees } = stack.last_mut(); - + let delimiter = builder.expected_delimiter().map(|it| it.kind); let tt = match token.as_leaf() { - Some(leaf) => tt::TokenTree::Leaf(leaf.clone()), + Some(leaf) => leaf.clone(), None => match token.kind(conv) { // Desugar doc comments into doc attributes COMMENT => { let span = conv.span_for(abs_range); - if let Some(tokens) = conv.convert_doc_comment(&token, span) { - token_trees.extend(tokens); - } + conv.convert_doc_comment(&token, span, &mut builder); continue; } kind if kind.is_punct() && kind != UNDERSCORE => { - let expected = match delimiter.kind { - tt::DelimiterKind::Parenthesis => Some(T![')']), - tt::DelimiterKind::Brace => Some(T!['}']), - tt::DelimiterKind::Bracket => Some(T![']']), - tt::DelimiterKind::Invisible => None, + let expected = match delimiter { + Some(tt::DelimiterKind::Parenthesis) => Some(T![')']), + Some(tt::DelimiterKind::Brace) => Some(T!['}']), + Some(tt::DelimiterKind::Bracket) => Some(T![']']), + Some(tt::DelimiterKind::Invisible) | None => None, }; // Current token is a closing delimiter that we expect, fix up the closing span // and end the subtree here if matches!(expected, Some(expected) if expected == kind) { - if let Some(mut subtree) = stack.pop() { - subtree.delimiter.close = conv.span_for(abs_range); - stack.last_mut().token_trees.push(subtree.build().into()); - } + builder.close(conv.span_for(abs_range)); continue; } @@ -268,16 +250,7 @@ where // Start a new subtree if let Some(kind) = delim { - let open = conv.span_for(abs_range); - stack.push(tt::SubtreeBuilder { - delimiter: tt::Delimiter { - open, - // will be overwritten on subtree close above - close: open, - kind, - }, - token_trees: vec![], - }); + builder.open(kind, conv.span_for(abs_range)); continue; } @@ -289,7 +262,6 @@ where panic!("Token from lexer must be single char: token = {token:#?}") }; tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }) - .into() } kind => { macro_rules! make_ident { @@ -320,7 +292,7 @@ where span: conv .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))), }); - token_trees.push(apostrophe.into()); + builder.push(apostrophe); let ident = tt::Leaf::from(tt::Ident { sym: Symbol::intern(&token.to_text(conv)[1..]), @@ -330,47 +302,26 @@ where )), is_raw: tt::IdentIsRaw::No, }); - token_trees.push(ident.into()); + builder.push(ident); continue; } _ => continue, }; - leaf.into() + leaf } }, }; - token_trees.push(tt); + builder.push(tt); } // If we get here, we've consumed all input tokens. // We might have more than one subtree in the stack, if the delimiters are improperly balanced. // Merge them so we're left with one. - while let Some(entry) = stack.pop() { - let parent = stack.last_mut(); - - let leaf: tt::Leaf<_> = tt::Punct { - span: entry.delimiter.open, - char: match entry.delimiter.kind { - tt::DelimiterKind::Parenthesis => '(', - tt::DelimiterKind::Brace => '{', - tt::DelimiterKind::Bracket => '[', - tt::DelimiterKind::Invisible => '$', - }, - spacing: tt::Spacing::Alone, - } - .into(); - parent.token_trees.push(leaf.into()); - parent.token_trees.extend(entry.token_trees); - } + builder.flatten_unclosed_subtrees(); - let subtree = stack.into_last().build(); - if let [tt::TokenTree::Subtree(first)] = &*subtree.token_trees { - first.clone() - } else { - subtree - } + builder.build_skip_top_subtree() } fn is_single_token_op(kind: SyntaxKind) -> bool { @@ -436,25 +387,17 @@ fn convert_doc_comment<S: Copy>( token: &syntax::SyntaxToken, span: S, mode: DocCommentDesugarMode, -) -> Option<Vec<tt::TokenTree<S>>> { - let comment = ast::Comment::cast(token.clone())?; - let doc = comment.kind().doc?; + builder: &mut tt::TopSubtreeBuilder<S>, +) { + let Some(comment) = ast::Comment::cast(token.clone()) else { return }; + let Some(doc) = comment.kind().doc else { return }; let mk_ident = |s: &str| { - tt::TokenTree::from(tt::Leaf::from(tt::Ident { - sym: Symbol::intern(s), - span, - is_raw: tt::IdentIsRaw::No, - })) + tt::Leaf::from(tt::Ident { sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No }) }; - let mk_punct = |c: char| { - tt::TokenTree::from(tt::Leaf::from(tt::Punct { - char: c, - spacing: tt::Spacing::Alone, - span, - })) - }; + let mk_punct = + |c: char| tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, span }); let mk_doc_literal = |comment: &ast::Comment| { let prefix_len = comment.prefix().len(); @@ -467,24 +410,20 @@ fn convert_doc_comment<S: Copy>( let (text, kind) = desugar_doc_comment_text(text, mode); let lit = tt::Literal { symbol: text, span, kind, suffix: None }; - tt::TokenTree::from(tt::Leaf::from(lit)) + tt::Leaf::from(lit) }; // Make `doc="\" Comments\"" - let meta_tkns = Box::new([mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]); + let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]; // Make `#![]` - let mut token_trees = Vec::with_capacity(3); - token_trees.push(mk_punct('#')); + builder.push(mk_punct('#')); if let ast::CommentPlacement::Inner = doc { - token_trees.push(mk_punct('!')); + builder.push(mk_punct('!')); } - token_trees.push(tt::TokenTree::from(tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket }, - token_trees: meta_tkns, - })); - - Some(token_trees) + builder.open(tt::DelimiterKind::Bracket, span); + builder.extend(meta_tkns); + builder.close(span); } /// A raw token (straight from lexer) converter @@ -518,7 +457,12 @@ trait SrcToken<Ctx, S> { trait TokenConverter<S>: Sized { type Token: SrcToken<Self, S>; - fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option<Vec<tt::TokenTree<S>>>; + fn convert_doc_comment( + &self, + token: &Self::Token, + span: S, + builder: &mut tt::TopSubtreeBuilder<S>, + ); fn bump(&mut self) -> Option<(Self::Token, TextRange)>; @@ -567,9 +511,10 @@ where &self, &token: &usize, span: SpanData<Ctx>, - ) -> Option<Vec<tt::TokenTree<SpanData<Ctx>>>> { + builder: &mut tt::TopSubtreeBuilder<SpanData<Ctx>>, + ) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode) + convert_doc_comment(&doc_comment(text), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -606,9 +551,9 @@ where { type Token = usize; - fn convert_doc_comment(&self, &token: &usize, span: S) -> Option<Vec<tt::TokenTree<S>>> { + fn convert_doc_comment(&self, &token: &usize, span: S, builder: &mut tt::TopSubtreeBuilder<S>) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode) + convert_doc_comment(&doc_comment(text), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -773,8 +718,13 @@ where SpanMap: SpanMapper<S>, { type Token = SynToken<S>; - fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option<Vec<tt::TokenTree<S>>> { - convert_doc_comment(token.token(), span, self.mode) + fn convert_doc_comment( + &self, + token: &Self::Token, + span: S, + builder: &mut tt::TopSubtreeBuilder<S>, + ) { + convert_doc_comment(token.token(), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -899,15 +849,12 @@ where /// This occurs when a float literal is used as a field access. fn float_split(&mut self, has_pseudo_dot: bool) { let (text, span) = match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf( - tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::Float, - suffix: _, - }), - _, - )) => (text.as_str(), *span), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::Float, + suffix: _, + }))) => (text.as_str(), *span), tt => unreachable!("{tt:?}"), }; // FIXME: Span splitting @@ -942,7 +889,7 @@ where } None => unreachable!(), } - self.cursor = self.cursor.bump(); + self.cursor.bump(); } fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) { @@ -950,24 +897,24 @@ where n_tokens = 2; } - let mut last = self.cursor; + let mut last_two = self.cursor.peek_two_leaves(); let mut combined_span = None; 'tokens: for _ in 0..n_tokens { let tmp: u8; if self.cursor.eof() { break; } - last = self.cursor; + last_two = self.cursor.peek_two_leaves(); let (text, span) = loop { break match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => match leaf { + Some(tt::TokenTree::Leaf(leaf)) => match leaf { tt::Leaf::Ident(ident) => { if ident.is_raw.yes() { self.buf.push_str("r#"); self.text_pos += TextSize::of("r#"); } let r = (ident.sym.as_str(), ident.span); - self.cursor = self.cursor.bump(); + self.cursor.bump(); r } tt::Leaf::Punct(punct) => { @@ -977,7 +924,7 @@ where std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(), punct.span, ); - self.cursor = self.cursor.bump(); + self.cursor.bump(); r } tt::Leaf::Literal(lit) => { @@ -989,20 +936,19 @@ where None => Some(lit.span), Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)), }; - self.cursor = self.cursor.bump(); + self.cursor.bump(); continue 'tokens; } }, - Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { - self.cursor = self.cursor.subtree().unwrap(); + Some(tt::TokenTree::Subtree(subtree)) => { + self.cursor.bump(); match delim_to_str(subtree.delimiter.kind, false) { Some(it) => (it, subtree.delimiter.open), None => continue, } } None => { - let parent = self.cursor.end().unwrap(); - self.cursor = self.cursor.bump(); + let parent = self.cursor.end(); match delim_to_str(parent.delimiter.kind, true) { Some(it) => (it, parent.delimiter.close), None => continue, @@ -1023,12 +969,7 @@ where self.buf.clear(); // FIXME: Emitting whitespace for this is really just a hack, we should get rid of it. // Add whitespace between adjoint puncts - let next = last.bump(); - if let ( - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)), - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)), - ) = (last.token_tree(), next.token_tree()) - { + if let Some([tt::Leaf::Punct(curr), tt::Leaf::Punct(next)]) = last_two { // Note: We always assume the semi-colon would be the last token in // other parts of RA such that we don't add whitespace here. // @@ -1058,7 +999,7 @@ where // We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts // but this runs afoul of our separation between `span` and `hir-expand`. SpanData { - range: if a.ctx == b.ctx { + range: if a.ctx == b.ctx && a.anchor == b.anchor { TextRange::new( std::cmp::min(a.range.start(), b.range.start()), std::cmp::max(a.range.end(), b.range.end()), diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs index 7b8e3f2b49c..d37cb508de1 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs @@ -2,10 +2,7 @@ use rustc_hash::FxHashMap; use span::Span; use syntax::{ast, AstNode}; use test_utils::extract_annotations; -use tt::{ - buffer::{TokenBuffer, TokenTreeRef}, - Leaf, Punct, Spacing, -}; +use tt::{buffer::Cursor, Leaf, Punct, Spacing}; use crate::{ dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, @@ -32,22 +29,22 @@ fn check_punct_spacing(fixture: &str) { }) .collect(); - let buf = TokenBuffer::from_subtree(&subtree); - let mut cursor = buf.begin(); + let mut cursor = Cursor::new(&subtree.0); while !cursor.eof() { while let Some(token_tree) = cursor.token_tree() { - if let TokenTreeRef::Leaf( - Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }), - _, - ) = token_tree + if let tt::TokenTree::Leaf(Leaf::Punct(Punct { + spacing, + span: Span { range, .. }, + .. + })) = token_tree { if let Some(expected) = annotations.remove(range) { assert_eq!(expected, *spacing); } } - cursor = cursor.bump_subtree(); + cursor.bump(); } - cursor = cursor.bump(); + cursor.bump_or_end(); } assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}"); diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs index 14216e30932..1bbb05f5507 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs @@ -6,37 +6,34 @@ use std::fmt; use span::Edition; use syntax::{SyntaxKind, SyntaxKind::*, T}; -use tt::buffer::TokenBuffer; - pub fn to_parser_input<S: Copy + fmt::Debug>( edition: Edition, - buffer: &TokenBuffer<'_, S>, + buffer: tt::TokenTreesView<'_, S>, ) -> parser::Input { let mut res = parser::Input::default(); - let mut current = buffer.begin(); + let mut current = buffer.cursor(); while !current.eof() { - let cursor = current; - let tt = cursor.token_tree(); + let tt = current.token_tree(); // Check if it is lifetime - if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt { if punct.char == '\'' { - let next = cursor.bump(); - match next.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(_ident), _)) => { + current.bump(); + match current.token_tree() { + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => { res.push(LIFETIME_IDENT); - current = next.bump(); + current.bump(); continue; } - _ => panic!("Next token must be ident : {:#?}", next.token_tree()), + _ => panic!("Next token must be ident"), } } } - current = match tt { - Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { + match tt { + Some(tt::TokenTree::Leaf(leaf)) => { match leaf { tt::Leaf::Literal(lit) => { let kind = match lit.kind { @@ -83,9 +80,9 @@ pub fn to_parser_input<S: Copy + fmt::Debug>( } } } - cursor.bump() + current.bump(); } - Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { + Some(tt::TokenTree::Subtree(subtree)) => { if let Some(kind) = match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => Some(T!['(']), tt::DelimiterKind::Brace => Some(T!['{']), @@ -94,22 +91,19 @@ pub fn to_parser_input<S: Copy + fmt::Debug>( } { res.push(kind); } - cursor.subtree().unwrap() + current.bump(); } - None => match cursor.end() { - Some(subtree) => { - if let Some(kind) = match subtree.delimiter.kind { - tt::DelimiterKind::Parenthesis => Some(T![')']), - tt::DelimiterKind::Brace => Some(T!['}']), - tt::DelimiterKind::Bracket => Some(T![']']), - tt::DelimiterKind::Invisible => None, - } { - res.push(kind); - } - cursor.bump() + None => { + let subtree = current.end(); + if let Some(kind) = match subtree.delimiter.kind { + tt::DelimiterKind::Parenthesis => Some(T![')']), + tt::DelimiterKind::Brace => Some(T!['}']), + tt::DelimiterKind::Bracket => Some(T![']']), + tt::DelimiterKind::Invisible => None, + } { + res.push(kind); } - None => continue, - }, + } }; } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 32b1f5f7544..72a46f2f9f0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -42,6 +42,14 @@ pub use self::{ /// the same representation: a pointer to the tree root and a pointer to the /// node itself. pub trait AstNode { + /// This panics if the `SyntaxKind` is not statically known. + fn kind() -> SyntaxKind + where + Self: Sized, + { + panic!("dynamic `SyntaxKind` for `AstNode::kind()`") + } + fn can_cast(kind: SyntaxKind) -> bool where Self: Sized; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index ffe9f16cfd5..291fc646e21 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -751,7 +751,7 @@ impl ast::MatchArmList { ted::insert_all(position, elements); fn needs_comma(arm: &ast::MatchArm) -> bool { - arm.expr().map_or(false, |e| !e.is_block_like()) && arm.comma_token().is_none() + arm.expr().is_some_and(|e| !e.is_block_like()) && arm.comma_token().is_none() } } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index f3053f59836..9466755576b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -393,7 +393,7 @@ impl ast::BlockExpr { FOR_EXPR | IF_EXPR => parent .children() .find(|it| ast::Expr::can_cast(it.kind())) - .map_or(true, |it| it == *self.syntax()), + .is_none_or(|it| it == *self.syntax()), LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false, _ => true, } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 3876ef71a07..69e2a9f9c1b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -2503,6 +2503,13 @@ pub struct AnyHasVisibility { impl ast::HasVisibility for AnyHasVisibility {} impl AstNode for Abi { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ABI + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ABI } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2517,6 +2524,13 @@ impl AstNode for Abi { } impl AstNode for ArgList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARG_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARG_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2531,6 +2545,13 @@ impl AstNode for ArgList { } impl AstNode for ArrayExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARRAY_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2545,6 +2566,13 @@ impl AstNode for ArrayExpr { } impl AstNode for ArrayType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARRAY_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2559,6 +2587,13 @@ impl AstNode for ArrayType { } impl AstNode for AsmClobberAbi { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_CLOBBER_ABI + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2573,6 +2608,13 @@ impl AstNode for AsmClobberAbi { } impl AstNode for AsmConst { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_CONST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2587,6 +2629,13 @@ impl AstNode for AsmConst { } impl AstNode for AsmDirSpec { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_DIR_SPEC + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2601,6 +2650,13 @@ impl AstNode for AsmDirSpec { } impl AstNode for AsmExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2615,6 +2671,13 @@ impl AstNode for AsmExpr { } impl AstNode for AsmLabel { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_LABEL + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2629,6 +2692,13 @@ impl AstNode for AsmLabel { } impl AstNode for AsmOperandExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPERAND_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2643,6 +2713,13 @@ impl AstNode for AsmOperandExpr { } impl AstNode for AsmOperandNamed { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPERAND_NAMED + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2657,6 +2734,13 @@ impl AstNode for AsmOperandNamed { } impl AstNode for AsmOption { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPTION + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2671,6 +2755,13 @@ impl AstNode for AsmOption { } impl AstNode for AsmOptions { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPTIONS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2685,6 +2776,13 @@ impl AstNode for AsmOptions { } impl AstNode for AsmRegOperand { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_REG_OPERAND + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2699,6 +2797,13 @@ impl AstNode for AsmRegOperand { } impl AstNode for AsmRegSpec { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_REG_SPEC + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2713,6 +2818,13 @@ impl AstNode for AsmRegSpec { } impl AstNode for AsmSym { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_SYM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2727,6 +2839,13 @@ impl AstNode for AsmSym { } impl AstNode for AssocItemList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASSOC_ITEM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2741,6 +2860,13 @@ impl AstNode for AssocItemList { } impl AstNode for AssocTypeArg { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASSOC_TYPE_ARG + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_TYPE_ARG } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2755,6 +2881,13 @@ impl AstNode for AssocTypeArg { } impl AstNode for Attr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ATTR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ATTR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2769,6 +2902,13 @@ impl AstNode for Attr { } impl AstNode for AwaitExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + AWAIT_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2783,6 +2923,13 @@ impl AstNode for AwaitExpr { } impl AstNode for BecomeExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BECOME_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BECOME_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2797,6 +2944,13 @@ impl AstNode for BecomeExpr { } impl AstNode for BinExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BIN_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2811,6 +2965,13 @@ impl AstNode for BinExpr { } impl AstNode for BlockExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BLOCK_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2825,6 +2986,13 @@ impl AstNode for BlockExpr { } impl AstNode for BoxPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BOX_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2839,6 +3007,13 @@ impl AstNode for BoxPat { } impl AstNode for BreakExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BREAK_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BREAK_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2853,6 +3028,13 @@ impl AstNode for BreakExpr { } impl AstNode for CallExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CALL_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CALL_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2867,6 +3049,13 @@ impl AstNode for CallExpr { } impl AstNode for CastExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CAST_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CAST_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2881,6 +3070,13 @@ impl AstNode for CastExpr { } impl AstNode for ClosureBinder { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CLOSURE_BINDER + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2895,6 +3091,13 @@ impl AstNode for ClosureBinder { } impl AstNode for ClosureExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CLOSURE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2909,6 +3112,13 @@ impl AstNode for ClosureExpr { } impl AstNode for Const { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2923,6 +3133,13 @@ impl AstNode for Const { } impl AstNode for ConstArg { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_ARG + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_ARG } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2937,6 +3154,13 @@ impl AstNode for ConstArg { } impl AstNode for ConstBlockPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_BLOCK_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_BLOCK_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2951,6 +3175,13 @@ impl AstNode for ConstBlockPat { } impl AstNode for ConstParam { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_PARAM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_PARAM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2965,6 +3196,13 @@ impl AstNode for ConstParam { } impl AstNode for ContinueExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONTINUE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONTINUE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2979,6 +3217,13 @@ impl AstNode for ContinueExpr { } impl AstNode for DynTraitType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + DYN_TRAIT_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == DYN_TRAIT_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2993,6 +3238,13 @@ impl AstNode for DynTraitType { } impl AstNode for Enum { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ENUM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ENUM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3007,6 +3259,13 @@ impl AstNode for Enum { } impl AstNode for ExprStmt { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXPR_STMT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3021,6 +3280,13 @@ impl AstNode for ExprStmt { } impl AstNode for ExternBlock { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_BLOCK + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_BLOCK } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3035,6 +3301,13 @@ impl AstNode for ExternBlock { } impl AstNode for ExternCrate { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_CRATE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_CRATE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3049,6 +3322,13 @@ impl AstNode for ExternCrate { } impl AstNode for ExternItemList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_ITEM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_ITEM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3063,6 +3343,13 @@ impl AstNode for ExternItemList { } impl AstNode for FieldExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FIELD_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3077,6 +3364,13 @@ impl AstNode for FieldExpr { } impl AstNode for Fn { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FN + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FN } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3091,6 +3385,13 @@ impl AstNode for Fn { } impl AstNode for FnPtrType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FN_PTR_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FN_PTR_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3105,6 +3406,13 @@ impl AstNode for FnPtrType { } impl AstNode for ForExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FOR_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3119,6 +3427,13 @@ impl AstNode for ForExpr { } impl AstNode for ForType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FOR_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3133,6 +3448,13 @@ impl AstNode for ForType { } impl AstNode for FormatArgsArg { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FORMAT_ARGS_ARG + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3147,6 +3469,13 @@ impl AstNode for FormatArgsArg { } impl AstNode for FormatArgsExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FORMAT_ARGS_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3161,6 +3490,13 @@ impl AstNode for FormatArgsExpr { } impl AstNode for GenericArgList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + GENERIC_ARG_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_ARG_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3175,6 +3511,13 @@ impl AstNode for GenericArgList { } impl AstNode for GenericParamList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + GENERIC_PARAM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3189,6 +3532,13 @@ impl AstNode for GenericParamList { } impl AstNode for IdentPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IDENT_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3203,6 +3553,13 @@ impl AstNode for IdentPat { } impl AstNode for IfExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IF_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3217,6 +3574,13 @@ impl AstNode for IfExpr { } impl AstNode for Impl { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IMPL + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3231,6 +3595,13 @@ impl AstNode for Impl { } impl AstNode for ImplTraitType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IMPL_TRAIT_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_TRAIT_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3245,6 +3616,13 @@ impl AstNode for ImplTraitType { } impl AstNode for IndexExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + INDEX_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == INDEX_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3259,6 +3637,13 @@ impl AstNode for IndexExpr { } impl AstNode for InferType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + INFER_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == INFER_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3273,6 +3658,13 @@ impl AstNode for InferType { } impl AstNode for ItemList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ITEM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ITEM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3287,6 +3679,13 @@ impl AstNode for ItemList { } impl AstNode for Label { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LABEL + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3301,6 +3700,13 @@ impl AstNode for Label { } impl AstNode for LetElse { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_ELSE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3315,6 +3721,13 @@ impl AstNode for LetElse { } impl AstNode for LetExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3329,6 +3742,13 @@ impl AstNode for LetExpr { } impl AstNode for LetStmt { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_STMT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_STMT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3343,6 +3763,13 @@ impl AstNode for LetStmt { } impl AstNode for Lifetime { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3357,6 +3784,13 @@ impl AstNode for Lifetime { } impl AstNode for LifetimeArg { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME_ARG + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_ARG } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3371,6 +3805,13 @@ impl AstNode for LifetimeArg { } impl AstNode for LifetimeParam { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME_PARAM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_PARAM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3385,6 +3826,13 @@ impl AstNode for LifetimeParam { } impl AstNode for Literal { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LITERAL + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3399,6 +3847,13 @@ impl AstNode for Literal { } impl AstNode for LiteralPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LITERAL_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3413,6 +3868,13 @@ impl AstNode for LiteralPat { } impl AstNode for LoopExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LOOP_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LOOP_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3427,6 +3889,13 @@ impl AstNode for LoopExpr { } impl AstNode for MacroCall { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_CALL + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_CALL } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3441,6 +3910,13 @@ impl AstNode for MacroCall { } impl AstNode for MacroDef { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_DEF + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3455,6 +3931,13 @@ impl AstNode for MacroDef { } impl AstNode for MacroExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3469,6 +3952,13 @@ impl AstNode for MacroExpr { } impl AstNode for MacroItems { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_ITEMS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_ITEMS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3483,6 +3973,13 @@ impl AstNode for MacroItems { } impl AstNode for MacroPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3497,6 +3994,13 @@ impl AstNode for MacroPat { } impl AstNode for MacroRules { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_RULES + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_RULES } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3511,6 +4015,13 @@ impl AstNode for MacroRules { } impl AstNode for MacroStmts { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_STMTS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_STMTS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3525,6 +4036,13 @@ impl AstNode for MacroStmts { } impl AstNode for MacroType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3539,6 +4057,13 @@ impl AstNode for MacroType { } impl AstNode for MatchArm { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_ARM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3553,6 +4078,13 @@ impl AstNode for MatchArm { } impl AstNode for MatchArmList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_ARM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3567,6 +4099,13 @@ impl AstNode for MatchArmList { } impl AstNode for MatchExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3581,6 +4120,13 @@ impl AstNode for MatchExpr { } impl AstNode for MatchGuard { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_GUARD + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_GUARD } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3595,6 +4141,13 @@ impl AstNode for MatchGuard { } impl AstNode for Meta { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + META + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == META } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3609,6 +4162,13 @@ impl AstNode for Meta { } impl AstNode for MethodCallExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + METHOD_CALL_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3623,6 +4183,13 @@ impl AstNode for MethodCallExpr { } impl AstNode for Module { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MODULE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3637,6 +4204,13 @@ impl AstNode for Module { } impl AstNode for Name { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NAME + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NAME } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3651,6 +4225,13 @@ impl AstNode for Name { } impl AstNode for NameRef { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NAME_REF + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NAME_REF } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3665,6 +4246,13 @@ impl AstNode for NameRef { } impl AstNode for NeverType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NEVER_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3679,6 +4267,13 @@ impl AstNode for NeverType { } impl AstNode for OffsetOfExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + OFFSET_OF_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3693,6 +4288,13 @@ impl AstNode for OffsetOfExpr { } impl AstNode for OrPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + OR_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == OR_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3707,6 +4309,13 @@ impl AstNode for OrPat { } impl AstNode for Param { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARAM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3721,6 +4330,13 @@ impl AstNode for Param { } impl AstNode for ParamList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARAM_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3735,6 +4351,13 @@ impl AstNode for ParamList { } impl AstNode for ParenExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3749,6 +4372,13 @@ impl AstNode for ParenExpr { } impl AstNode for ParenPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3763,6 +4393,13 @@ impl AstNode for ParenPat { } impl AstNode for ParenType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3777,6 +4414,13 @@ impl AstNode for ParenType { } impl AstNode for ParenthesizedArgList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARENTHESIZED_ARG_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARENTHESIZED_ARG_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3791,6 +4435,13 @@ impl AstNode for ParenthesizedArgList { } impl AstNode for Path { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3805,6 +4456,13 @@ impl AstNode for Path { } impl AstNode for PathExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3819,6 +4477,13 @@ impl AstNode for PathExpr { } impl AstNode for PathPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3833,6 +4498,13 @@ impl AstNode for PathPat { } impl AstNode for PathSegment { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_SEGMENT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_SEGMENT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3847,6 +4519,13 @@ impl AstNode for PathSegment { } impl AstNode for PathType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3861,6 +4540,13 @@ impl AstNode for PathType { } impl AstNode for PrefixExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PREFIX_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PREFIX_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3875,6 +4561,13 @@ impl AstNode for PrefixExpr { } impl AstNode for PtrType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PTR_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PTR_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3889,6 +4582,13 @@ impl AstNode for PtrType { } impl AstNode for RangeExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RANGE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3903,6 +4603,13 @@ impl AstNode for RangeExpr { } impl AstNode for RangePat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RANGE_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3917,6 +4624,13 @@ impl AstNode for RangePat { } impl AstNode for RecordExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3931,6 +4645,13 @@ impl AstNode for RecordExpr { } impl AstNode for RecordExprField { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR_FIELD + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3945,6 +4666,13 @@ impl AstNode for RecordExprField { } impl AstNode for RecordExprFieldList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR_FIELD_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3959,6 +4687,13 @@ impl AstNode for RecordExprFieldList { } impl AstNode for RecordField { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_FIELD + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3973,6 +4708,13 @@ impl AstNode for RecordField { } impl AstNode for RecordFieldList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_FIELD_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3987,6 +4729,13 @@ impl AstNode for RecordFieldList { } impl AstNode for RecordPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4001,6 +4750,13 @@ impl AstNode for RecordPat { } impl AstNode for RecordPatField { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT_FIELD + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4015,6 +4771,13 @@ impl AstNode for RecordPatField { } impl AstNode for RecordPatFieldList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT_FIELD_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4029,6 +4792,13 @@ impl AstNode for RecordPatFieldList { } impl AstNode for RefExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4043,6 +4813,13 @@ impl AstNode for RefExpr { } impl AstNode for RefPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4057,6 +4834,13 @@ impl AstNode for RefPat { } impl AstNode for RefType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4071,6 +4855,13 @@ impl AstNode for RefType { } impl AstNode for Rename { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RENAME + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RENAME } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4085,6 +4876,13 @@ impl AstNode for Rename { } impl AstNode for RestPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REST_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REST_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4099,6 +4897,13 @@ impl AstNode for RestPat { } impl AstNode for RetType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RET_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RET_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4113,6 +4918,13 @@ impl AstNode for RetType { } impl AstNode for ReturnExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RETURN_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4127,6 +4939,13 @@ impl AstNode for ReturnExpr { } impl AstNode for ReturnTypeSyntax { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RETURN_TYPE_SYNTAX + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_SYNTAX } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4141,6 +4960,13 @@ impl AstNode for ReturnTypeSyntax { } impl AstNode for SelfParam { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SELF_PARAM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SELF_PARAM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4155,6 +4981,13 @@ impl AstNode for SelfParam { } impl AstNode for SlicePat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SLICE_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4169,6 +5002,13 @@ impl AstNode for SlicePat { } impl AstNode for SliceType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SLICE_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4183,6 +5023,13 @@ impl AstNode for SliceType { } impl AstNode for SourceFile { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SOURCE_FILE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4197,6 +5044,13 @@ impl AstNode for SourceFile { } impl AstNode for Static { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STATIC + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STATIC } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4211,6 +5065,13 @@ impl AstNode for Static { } impl AstNode for StmtList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STMT_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4225,6 +5086,13 @@ impl AstNode for StmtList { } impl AstNode for Struct { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STRUCT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STRUCT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4239,6 +5107,13 @@ impl AstNode for Struct { } impl AstNode for TokenTree { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TOKEN_TREE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4253,6 +5128,13 @@ impl AstNode for TokenTree { } impl AstNode for Trait { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRAIT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4267,6 +5149,13 @@ impl AstNode for Trait { } impl AstNode for TraitAlias { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRAIT_ALIAS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4281,6 +5170,13 @@ impl AstNode for TraitAlias { } impl AstNode for TryExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRY_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4295,6 +5191,13 @@ impl AstNode for TryExpr { } impl AstNode for TupleExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4309,6 +5212,13 @@ impl AstNode for TupleExpr { } impl AstNode for TupleField { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_FIELD + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4323,6 +5233,13 @@ impl AstNode for TupleField { } impl AstNode for TupleFieldList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_FIELD_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4337,6 +5254,13 @@ impl AstNode for TupleFieldList { } impl AstNode for TuplePat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4351,6 +5275,13 @@ impl AstNode for TuplePat { } impl AstNode for TupleStructPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_STRUCT_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_STRUCT_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4365,6 +5296,13 @@ impl AstNode for TupleStructPat { } impl AstNode for TupleType { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_TYPE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_TYPE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4379,6 +5317,13 @@ impl AstNode for TupleType { } impl AstNode for TypeAlias { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_ALIAS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4393,6 +5338,13 @@ impl AstNode for TypeAlias { } impl AstNode for TypeArg { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_ARG + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ARG } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4407,6 +5359,13 @@ impl AstNode for TypeArg { } impl AstNode for TypeBound { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_BOUND + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4421,6 +5380,13 @@ impl AstNode for TypeBound { } impl AstNode for TypeBoundList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_BOUND_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4435,6 +5401,13 @@ impl AstNode for TypeBoundList { } impl AstNode for TypeParam { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_PARAM + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_PARAM } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4449,6 +5422,13 @@ impl AstNode for TypeParam { } impl AstNode for UnderscoreExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + UNDERSCORE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == UNDERSCORE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4463,6 +5443,13 @@ impl AstNode for UnderscoreExpr { } impl AstNode for Union { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + UNION + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == UNION } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4477,6 +5464,13 @@ impl AstNode for Union { } impl AstNode for Use { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4491,6 +5485,13 @@ impl AstNode for Use { } impl AstNode for UseBoundGenericArgs { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_BOUND_GENERIC_ARGS + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4505,6 +5506,13 @@ impl AstNode for UseBoundGenericArgs { } impl AstNode for UseTree { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_TREE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4519,6 +5527,13 @@ impl AstNode for UseTree { } impl AstNode for UseTreeList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_TREE_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4533,6 +5548,13 @@ impl AstNode for UseTreeList { } impl AstNode for Variant { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VARIANT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4547,6 +5569,13 @@ impl AstNode for Variant { } impl AstNode for VariantList { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VARIANT_LIST + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT_LIST } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4561,6 +5590,13 @@ impl AstNode for VariantList { } impl AstNode for Visibility { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VISIBILITY + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4575,6 +5611,13 @@ impl AstNode for Visibility { } impl AstNode for WhereClause { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHERE_CLAUSE + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4589,6 +5632,13 @@ impl AstNode for WhereClause { } impl AstNode for WherePred { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHERE_PRED + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_PRED } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4603,6 +5653,13 @@ impl AstNode for WherePred { } impl AstNode for WhileExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHILE_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHILE_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4617,6 +5674,13 @@ impl AstNode for WhileExpr { } impl AstNode for WildcardPat { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WILDCARD_PAT + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WILDCARD_PAT } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4631,6 +5695,13 @@ impl AstNode for WildcardPat { } impl AstNode for YeetExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + YEET_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == YEET_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -4645,6 +5716,13 @@ impl AstNode for YeetExpr { } impl AstNode for YieldExpr { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + YIELD_EXPR + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == YIELD_EXPR } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index eb96ab6ef59..282cbc4b3a4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -8,7 +8,10 @@ //! Keep in mind that `from_text` functions should be kept private. The public //! API should require to assemble every node piecewise. The trick of //! `parse(format!())` we use internally is an implementation detail -- long -//! term, it will be replaced with direct tree manipulation. +//! term, it will be replaced with `quote!`. Do not add more usages to `from_text` - +//! use `quote!` instead. + +mod quote; use itertools::Itertools; use parser::{Edition, T}; @@ -16,7 +19,7 @@ use rowan::NodeOrToken; use stdx::{format_to, format_to_acc, never}; use crate::{ - ast::{self, Param}, + ast::{self, make::quote::quote, Param}, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken, }; @@ -118,7 +121,11 @@ pub fn name(name: &str) -> ast::Name { } pub fn name_ref(name_ref: &str) -> ast::NameRef { let raw_escape = raw_ident_esc(name_ref); - ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}")) + quote! { + NameRef { + [IDENT format!("{raw_escape}{name_ref}")] + } + } } fn raw_ident_esc(ident: &str) -> &'static str { if is_raw_identifier(ident, Edition::CURRENT) { @@ -135,7 +142,11 @@ pub fn lifetime(text: &str) -> ast::Lifetime { tmp = format!("'{text}"); text = &tmp; } - ast_from_text(&format!("fn f<{text}>() {{ }}")) + quote! { + Lifetime { + [LIFETIME_IDENT text] + } + } } // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la @@ -175,63 +186,37 @@ pub fn ty_alias( where_clause: Option<ast::WhereClause>, assignment: Option<(ast::Type, Option<ast::WhereClause>)>, ) -> ast::TypeAlias { - let mut s = String::new(); - s.push_str(&format!("type {ident}")); - - if let Some(list) = generic_param_list { - s.push_str(&list.to_string()); - } - - if let Some(list) = type_param_bounds { - s.push_str(&format!(" : {list}")); - } - - if let Some(cl) = where_clause { - s.push_str(&format!(" {cl}")); - } - - if let Some(exp) = assignment { - if let Some(cl) = exp.1 { - s.push_str(&format!(" = {} {cl}", exp.0)); - } else { - s.push_str(&format!(" = {}", exp.0)); + let (assignment_ty, assignment_where) = assignment.unzip(); + let assignment_where = assignment_where.flatten(); + quote! { + TypeAlias { + [type] " " + Name { [IDENT ident] } + #generic_param_list + #(" " [:] " " #type_param_bounds)* + #(" " #where_clause)* + #(" " [=] " " #assignment_ty)* + #(" " #assignment_where)* + [;] } } - - s.push(';'); - ast_from_text(&s) } pub fn ty_fn_ptr<I: Iterator<Item = Param>>( - for_lifetime_list: Option<ast::GenericParamList>, is_unsafe: bool, abi: Option<ast::Abi>, - params: I, + mut params: I, ret_type: Option<ast::RetType>, ) -> ast::FnPtrType { - let mut s = String::from("type __ = "); - - if let Some(list) = for_lifetime_list { - format_to!(s, "for{} ", list); - } - - if is_unsafe { - s.push_str("unsafe "); - } - - if let Some(abi) = abi { - format_to!(s, "{} ", abi) - } - - s.push_str("fn"); - - format_to!(s, "({})", params.map(|p| p.to_string()).join(", ")); - - if let Some(ret_type) = ret_type { - format_to!(s, " {}", ret_type); + let is_unsafe = is_unsafe.then_some(()); + let first_param = params.next(); + quote! { + FnPtrType { + #(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn] + ['('] #first_param #([,] " " #params)* [')'] + #(" " #ret_type)* + } } - - ast_from_text(&s) } pub fn assoc_item_list() -> ast::AssocItemList { @@ -351,6 +336,24 @@ pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { ast_from_text(&format!("type __ = {name_ref};")) } +/// Type and expressions/patterns path differ in whether they require `::` before generic arguments. +/// Type paths allow them but they are often omitted, while expression/pattern paths require them. +pub fn generic_ty_path_segment( + name_ref: ast::NameRef, + generic_args: impl IntoIterator<Item = ast::GenericArg>, +) -> ast::PathSegment { + let mut generic_args = generic_args.into_iter(); + let first_generic_arg = generic_args.next(); + quote! { + PathSegment { + #name_ref + GenericArgList { + [<] #first_generic_arg #([,] " " #generic_args)* [>] + } + } + } +} + pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment { let text = match trait_ref { Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"), @@ -480,15 +483,16 @@ pub fn block_expr( stmts: impl IntoIterator<Item = ast::Stmt>, tail_expr: Option<ast::Expr>, ) -> ast::BlockExpr { - let mut buf = "{\n".to_owned(); - for stmt in stmts.into_iter() { - format_to!(buf, " {stmt}\n"); - } - if let Some(tail_expr) = tail_expr { - format_to!(buf, " {tail_expr}\n"); + quote! { + BlockExpr { + StmtList { + ['{'] "\n" + #(" " #stmts "\n")* + #(" " #tail_expr "\n")* + ['}'] + } + } } - buf += "}"; - ast_from_text(&format!("fn f() {buf}")) } pub fn async_move_block_expr( @@ -815,7 +819,7 @@ pub fn match_arm_with_guard( pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { - let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like()); + let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like()); let comma = if needs_comma { "," } else { "" }; let arm = arm.syntax(); format_to_acc!(acc, " {arm}{comma}\n") @@ -828,7 +832,7 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat } pub fn where_pred( - path: ast::Path, + path: ast::Type, bounds: impl IntoIterator<Item = ast::TypeBound>, ) -> ast::WherePred { let bounds = bounds.into_iter().join(" + "); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs new file mode 100644 index 00000000000..300ef25c137 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs @@ -0,0 +1,191 @@ +//! A `quote!`-like API for crafting AST nodes. + +pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken, SyntaxKind as RSyntaxKind}; + +macro_rules! quote_impl_ { + ( @append $children:ident ) => {}; // Base case. + + ( @append $children:ident + $node:ident { + $($tree:tt)* + } + $($rest:tt)* + ) => { + { + #[allow(unused_mut)] + let mut inner_children = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken< + $crate::ast::make::quote::GreenNode, + $crate::ast::make::quote::GreenToken, + >>::new(); + $crate::ast::make::quote::quote_impl!( @append inner_children + $($tree)* + ); + let kind = <$crate::ast::$node as $crate::ast::AstNode>::kind(); + let node = $crate::ast::make::quote::GreenNode::new($crate::ast::make::quote::RSyntaxKind(kind as u16), inner_children); + $children.push($crate::ast::make::quote::NodeOrToken::Node(node)); + } + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + [ $token_kind:ident $token_text:expr ] + $($rest:tt)* + ) => { + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::$token_kind as u16), + &$token_text, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + [$($token:tt)+] + $($rest:tt)* + ) => { + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::T![ $($token)+ ] as u16), + const { $crate::T![ $($token)+ ].text() }, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + $whitespace:literal + $($rest:tt)* + ) => { + const { $crate::ast::make::quote::verify_only_whitespaces($whitespace) }; + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::WHITESPACE as u16), + $whitespace, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + # $var:ident + $($rest:tt)* + ) => { + $crate::ast::make::quote::ToNodeChild::append_node_child($var, &mut $children); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + #( $($repetition:tt)+ )* + $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [] [] $($repetition)* + ); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + // Base case - no repetition var. + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ ] + ) => { + ::std::compile_error!("repetition in `ast::make::quote!()` without variable"); + }; + + // Base case - repetition var found. + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $repetition_var:ident ] + ) => { + ::std::iter::IntoIterator::into_iter($repetition_var).for_each(|$repetition_var| { + $crate::ast::make::quote::quote_impl!( @append $children $($repetition)* ); + }); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $repetition_var1:ident ] # $repetition_var2:ident $($rest:tt)* + ) => { + ::std::compile_error!("repetition in `ast::make::quote!()` with more than one variable"); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ ] # $repetition_var:ident $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [ $($repetition)* # $repetition_var ] [ $repetition_var ] $($rest)* + ); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $($repetition_var:tt)* ] $non_repetition_var:tt $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [ $($repetition)* $non_repetition_var ] [ $($repetition_var)* ] $($rest)* + ); + }; +} +pub(crate) use quote_impl_ as quote_impl; + +/// A `quote!`-like API for crafting AST nodes. +/// +/// Syntax: AST nodes are created with `Node { children }`, where `Node` is the node name in `ast` (`ast::Node`). +/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Alternatively, tokens can +/// be created with the syntax `[token_kind token_text]`, where `token_kind` is a variant of `SyntaxKind` (e.g. +/// `IDENT`) and `token_text` is an expression producing `String` or `&str`. Whitespaces can be added +/// as string literals (i.e. `"\n "` is a whitespace token). Interpolation is allowed with `#` (`#variable`), +/// from `AstNode`s and `Option`s of them. Repetition is also supported, with only one repeating variable +/// and no separator (`#("\n" #variable [>])*`), for any `IntoIterator`. Note that `Option`s are also `IntoIterator`, +/// which can help when you want to conditionally include something along with an optional node. +/// +/// There needs to be one root node, and its type is returned. +/// +/// Be careful to closely match the Ungrammar AST, there is no validation for this! +macro_rules! quote_ { + ( $root:ident { $($tree:tt)* } ) => {{ + #[allow(unused_mut)] + let mut root = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken< + $crate::ast::make::quote::GreenNode, + $crate::ast::make::quote::GreenToken, + >>::with_capacity(1); + $crate::ast::make::quote::quote_impl!( @append root $root { $($tree)* } ); + let root = root.into_iter().next().unwrap(); + let root = $crate::SyntaxNode::new_root(root.into_node().unwrap()); + <$crate::ast::$root as $crate::ast::AstNode>::cast(root).unwrap() + }}; +} +pub(crate) use quote_ as quote; + +use crate::AstNode; + +pub(crate) trait ToNodeChild { + fn append_node_child(self, children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>); +} + +impl<N: AstNode> ToNodeChild for N { + fn append_node_child(self, children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) { + children.push((*self.syntax().clone_subtree().green()).to_owned().into()); + } +} + +impl<C: ToNodeChild> ToNodeChild for Option<C> { + fn append_node_child(self, children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) { + if let Some(child) = self { + child.append_node_child(children); + } + } +} + +// This is useful when you want conditionally, based on some `bool`, to emit some code. +impl ToNodeChild for () { + fn append_node_child(self, _children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) {} +} + +pub(crate) const fn verify_only_whitespaces(text: &str) { + let text = text.as_bytes(); + let mut i = 0; + while i < text.len() { + if !text[i].is_ascii_whitespace() { + panic!("non-whitespace found in whitespace token"); + } + i += 1; + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 6ec73e76f78..81c7e15bcbc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -333,7 +333,7 @@ impl ast::Path { impl ast::Use { pub fn is_simple_glob(&self) -> bool { - self.use_tree().map_or(false, |use_tree| { + self.use_tree().is_some_and(|use_tree| { use_tree.use_tree_list().is_none() && use_tree.star_token().is_some() }) } @@ -387,7 +387,7 @@ impl ast::UseTreeList { if let Some((single_subtree,)) = u.use_trees().collect_tuple() { // We have a single subtree, check whether it is self. - let is_self = single_subtree.path().as_ref().map_or(false, |path| { + let is_self = single_subtree.path().as_ref().is_some_and(|path| { path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none() }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 280c5c25cb9..bea6bfeafcf 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,6 +1,4 @@ //! Wrappers over [`make`] constructors -use itertools::Itertools; - use crate::{ ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, syntax_editor::SyntaxMappingBuilder, @@ -62,13 +60,12 @@ impl SyntaxFactory { pub fn block_expr( &self, - stmts: impl IntoIterator<Item = ast::Stmt>, + statements: impl IntoIterator<Item = ast::Stmt>, tail_expr: Option<ast::Expr>, ) -> ast::BlockExpr { - let stmts = stmts.into_iter().collect_vec(); - let mut input = stmts.iter().map(|it| it.syntax().clone()).collect_vec(); + let (statements, mut input) = iterator_input(statements); - let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update(); + let ast = make::block_expr(statements, tail_expr.clone()).clone_for_update(); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -257,14 +254,15 @@ impl SyntaxFactory { pub fn turbofish_generic_arg_list( &self, - args: impl IntoIterator<Item = ast::GenericArg> + Clone, + generic_args: impl IntoIterator<Item = ast::GenericArg>, ) -> ast::GenericArgList { - let ast = make::turbofish_generic_arg_list(args.clone()).clone_for_update(); + let (generic_args, input) = iterator_input(generic_args); + let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_children( - args.into_iter().map(|arg| arg.syntax().clone()), + input.into_iter(), ast.generic_args().map(|arg| arg.syntax().clone()), ); builder.finish(&mut mapping); @@ -277,8 +275,7 @@ impl SyntaxFactory { &self, fields: impl IntoIterator<Item = ast::RecordField>, ) -> ast::RecordFieldList { - let fields: Vec<ast::RecordField> = fields.into_iter().collect(); - let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect(); + let (fields, input) = iterator_input(fields); let ast = make::record_field_list(fields).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -323,8 +320,7 @@ impl SyntaxFactory { &self, fields: impl IntoIterator<Item = ast::TupleField>, ) -> ast::TupleFieldList { - let fields: Vec<ast::TupleField> = fields.into_iter().collect(); - let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect(); + let (fields, input) = iterator_input(fields); let ast = make::tuple_field_list(fields).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -419,8 +415,7 @@ impl SyntaxFactory { &self, variants: impl IntoIterator<Item = ast::Variant>, ) -> ast::VariantList { - let variants: Vec<ast::Variant> = variants.into_iter().collect(); - let input: Vec<_> = variants.iter().map(|it| it.syntax().clone()).collect(); + let (variants, input) = iterator_input(variants); let ast = make::variant_list(variants).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -481,7 +476,7 @@ impl SyntaxFactory { pub fn token_tree( &self, delimiter: SyntaxKind, - tt: Vec<NodeOrToken<ast::TokenTree, SyntaxToken>>, + tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>, ) -> ast::TokenTree { let tt: Vec<_> = tt.into_iter().collect(); let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect(); @@ -512,3 +507,20 @@ impl SyntaxFactory { make::tokens::whitespace(text) } } + +// We need to collect `input` here instead of taking `impl IntoIterator + Clone`, +// because if we took `impl IntoIterator + Clone`, that could be something like an +// `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor. +// +// In that case, the iterator would be evaluated inside of the call to `map_children`, +// and the inner constructor would try to take a mutable borrow of the mappings `RefCell`, +// which would panic since it's already being mutably borrowed in the outer constructor. +fn iterator_input<N: AstNode>(input: impl IntoIterator<Item = N>) -> (Vec<N>, Vec<SyntaxNode>) { + input + .into_iter() + .map(|it| { + let syntax = it.syntax().clone(); + (it, syntax) + }) + .collect() +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index df017ddde64..7d5ca270435 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -116,7 +116,7 @@ impl CommentKind { impl ast::Whitespace { pub fn spans_multiple_lines(&self) -> bool { let text = self.text(); - text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) + text.find('\n').is_some_and(|idx| text[idx + 1..].contains('\n')) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 8069fdd06f7..450d601615e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -24,7 +24,7 @@ impl SyntaxEditor { if last_param .syntax() .next_sibling_or_token() - .map_or(false, |it| it.kind() == SyntaxKind::COMMA) + .is_some_and(|it| it.kind() == SyntaxKind::COMMA) { self.insert( Position::after(last_param.syntax()), diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 889a7d10ada..0e72d796875 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -13,16 +13,18 @@ use hir_expand::{ proc_macro::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, }, - quote, FileRange, + quote, + tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter}, + FileRange, }; use intern::Symbol; use rustc_hash::FxHashMap; use span::{Edition, EditionedFileId, FileId, Span}; +use stdx::itertools::Itertools; use test_utils::{ extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER, }; -use tt::{Leaf, Subtree, TokenTree}; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); @@ -374,7 +376,7 @@ impl ChangeFixture { } } -fn default_test_proc_macros() -> [(String, ProcMacro); 6] { +fn default_test_proc_macros() -> [(String, ProcMacro); 8] { [ ( r#" @@ -466,6 +468,36 @@ pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream { disabled: false, }, ), + ( + r#" +#[proc_macro_attribute] +pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream { + loop {} +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_18840"), + kind: ProcMacroKind::Attr, + expander: sync::Arc::new(Issue18840ProcMacroExpander), + disabled: false, + }, + ), + ( + r#" +#[proc_macro] +pub fn issue_17479(input: TokenStream) -> TokenStream { + input +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_17479"), + kind: ProcMacroKind::Bang, + expander: sync::Arc::new(Issue17479ProcMacroExpander), + disabled: false, + }, + ), ] } @@ -580,14 +612,14 @@ struct IdentityProcMacroExpander; impl ProcMacroExpander for IdentityProcMacroExpander { fn expand( &self, - subtree: &Subtree<Span>, - _: Option<&Subtree<Span>>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option<String>, - ) -> Result<Subtree<Span>, ProcMacroExpansionError> { + ) -> Result<TopSubtree, ProcMacroExpansionError> { Ok(subtree.clone()) } } @@ -598,15 +630,17 @@ struct Issue18089ProcMacroExpander; impl ProcMacroExpander for Issue18089ProcMacroExpander { fn expand( &self, - subtree: &Subtree<Span>, - _: Option<&Subtree<Span>>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, call_site: Span, _: Span, _: Option<String>, - ) -> Result<Subtree<Span>, ProcMacroExpansionError> { - let macro_name = &subtree.token_trees[1]; + ) -> Result<TopSubtree, ProcMacroExpansionError> { + let tt::TokenTree::Leaf(macro_name) = &subtree.0[2] else { + return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned())); + }; Ok(quote! { call_site => #[macro_export] macro_rules! my_macro___ { @@ -627,14 +661,14 @@ struct AttributeInputReplaceProcMacroExpander; impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { fn expand( &self, - _: &Subtree<Span>, - attrs: Option<&Subtree<Span>>, + _: &TopSubtree, + attrs: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option<String>, - ) -> Result<Subtree<Span>, ProcMacroExpansionError> { + ) -> Result<TopSubtree, ProcMacroExpansionError> { attrs .cloned() .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) @@ -642,30 +676,64 @@ impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { } #[derive(Debug)] +struct Issue18840ProcMacroExpander; +impl ProcMacroExpander for Issue18840ProcMacroExpander { + fn expand( + &self, + fn_: &TopSubtree, + _: Option<&TopSubtree>, + _: &Env, + def_site: Span, + _: Span, + _: Span, + _: Option<String>, + ) -> Result<TopSubtree, ProcMacroExpansionError> { + // Input: + // ``` + // #[issue_18840] + // fn foo() { let loop {} } + // ``` + + // The span that was created by the fixup infra. + let fixed_up_span = fn_.token_trees().flat_tokens()[5].first_span(); + let mut result = + quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } }; + // Make it so we won't remove the top subtree when reversing fixups. + let top_subtree_delimiter_mut = result.top_subtree_delimiter_mut(); + top_subtree_delimiter_mut.open = def_site; + top_subtree_delimiter_mut.close = def_site; + Ok(result) + } +} + +#[derive(Debug)] struct MirrorProcMacroExpander; impl ProcMacroExpander for MirrorProcMacroExpander { fn expand( &self, - input: &Subtree<Span>, - _: Option<&Subtree<Span>>, + input: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option<String>, - ) -> Result<Subtree<Span>, ProcMacroExpansionError> { - fn traverse(input: &Subtree<Span>) -> Subtree<Span> { - let mut token_trees = vec![]; - for tt in input.token_trees.iter().rev() { - let tt = match tt { - tt::TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(leaf.clone()), - tt::TokenTree::Subtree(sub) => tt::TokenTree::Subtree(traverse(sub)), - }; - token_trees.push(tt); + ) -> Result<TopSubtree, ProcMacroExpansionError> { + fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) { + for tt in iter.collect_vec().into_iter().rev() { + match tt { + TtElement::Leaf(leaf) => builder.push(leaf.clone()), + TtElement::Subtree(subtree, subtree_iter) => { + builder.open(subtree.delimiter.kind, subtree.delimiter.open); + traverse(builder, subtree_iter); + builder.close(subtree.delimiter.close); + } + } } - Subtree { delimiter: input.delimiter, token_trees: token_trees.into_boxed_slice() } } - Ok(traverse(input)) + let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter); + traverse(&mut builder, input.iter()); + Ok(builder.build()) } } @@ -677,31 +745,24 @@ struct ShortenProcMacroExpander; impl ProcMacroExpander for ShortenProcMacroExpander { fn expand( &self, - input: &Subtree<Span>, - _: Option<&Subtree<Span>>, + input: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option<String>, - ) -> Result<Subtree<Span>, ProcMacroExpansionError> { - return Ok(traverse(input)); - - fn traverse(input: &Subtree<Span>) -> Subtree<Span> { - let token_trees = input - .token_trees - .iter() - .map(|it| match it { - TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(modify_leaf(leaf)), - TokenTree::Subtree(subtree) => tt::TokenTree::Subtree(traverse(subtree)), - }) - .collect(); - Subtree { delimiter: input.delimiter, token_trees } + ) -> Result<TopSubtree, ProcMacroExpansionError> { + let mut result = input.0.clone(); + for it in &mut result { + if let TokenTree::Leaf(leaf) = it { + modify_leaf(leaf) + } } + return Ok(tt::TopSubtree(result)); - fn modify_leaf(leaf: &Leaf<Span>) -> Leaf<Span> { - let mut leaf = leaf.clone(); - match &mut leaf { + fn modify_leaf(leaf: &mut Leaf) { + match leaf { Leaf::Literal(it) => { // XXX Currently replaces any literals with an empty string, but supporting // "shortening" other literals would be nice. @@ -712,7 +773,31 @@ impl ProcMacroExpander for ShortenProcMacroExpander { it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::<String>()); } } - leaf } } } + +// Reads ident type within string quotes, for issue #17479. +#[derive(Debug)] +struct Issue17479ProcMacroExpander; +impl ProcMacroExpander for Issue17479ProcMacroExpander { + fn expand( + &self, + subtree: &TopSubtree, + _: Option<&TopSubtree>, + _: &Env, + _: Span, + _: Span, + _: Span, + _: Option<String>, + ) -> Result<TopSubtree, ProcMacroExpansionError> { + let TokenTree::Leaf(Leaf::Literal(lit)) = &subtree.0[1] else { + return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); + }; + let symbol = &lit.symbol; + let span = lit.span; + Ok(quote! { span => + #symbol() + }) + } +} diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 99dfabe174e..4a2346193b4 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -17,6 +17,7 @@ //! builtin_impls: //! cell: copy, drop //! clone: sized +//! coerce_pointee: derive, sized, unsize, coerce_unsized, dispatch_from_dyn //! coerce_unsized: unsize //! concat: //! copy: clone @@ -157,6 +158,14 @@ pub mod marker { type Discriminant; } // endregion:discriminant + + // region:coerce_pointee + #[rustc_builtin_macro(CoercePointee, attributes(pointee))] + #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] + pub macro CoercePointee($item:item) { + /* compiler built-in */ + } + // endregion:coerce_pointee } // region:default diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index a0603e35a09..325b94cc33b 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -1,6 +1,12 @@ //! Discovery of `cargo` & `rustc` executables. -use std::{env, iter, path::PathBuf}; +use std::{ + env, + ffi::OsStr, + iter, + path::{Path, PathBuf}, + process::Command, +}; use camino::{Utf8Path, Utf8PathBuf}; @@ -65,6 +71,14 @@ impl Tool { } } +pub fn command(cmd: impl AsRef<OsStr>, working_directory: impl AsRef<Path>) -> Command { + // we are `toolchain::command`` + #[allow(clippy::disallowed_methods)] + let mut cmd = Command::new(cmd); + cmd.current_dir(working_directory); + cmd +} + fn invoke(list: &[fn(&str) -> Option<Utf8PathBuf>], executable: &str) -> Utf8PathBuf { list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into()) } @@ -102,7 +116,6 @@ fn lookup_in_path(exec: &str) -> Option<Utf8PathBuf> { let paths = env::var_os("PATH").unwrap_or_default(); env::split_paths(&paths) .map(|path| path.join(exec)) - .map(PathBuf::from) .map(Utf8PathBuf::try_from) .filter_map(Result::ok) .find_map(probe_for_binary) diff --git a/src/tools/rust-analyzer/crates/tt/src/buffer.rs b/src/tools/rust-analyzer/crates/tt/src/buffer.rs index acb7e2d6c51..02a722895a4 100644 --- a/src/tools/rust-analyzer/crates/tt/src/buffer.rs +++ b/src/tools/rust-analyzer/crates/tt/src/buffer.rs @@ -1,259 +1,108 @@ //! Stateful iteration over token trees. //! //! We use this as the source of tokens for parser. -use crate::{Leaf, Subtree, TokenTree}; +use crate::{Leaf, Subtree, TokenTree, TokenTreesView}; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct EntryId(usize); - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct EntryPtr( - /// The index of the buffer containing the entry. - EntryId, - /// The index of the entry within the buffer. - usize, -); - -/// Internal type which is used instead of `TokenTree` to represent a token tree -/// within a `TokenBuffer`. -#[derive(Debug)] -enum Entry<'t, Span> { - // Mimicking types from proc-macro. - Subtree(Option<&'t TokenTree<Span>>, &'t Subtree<Span>, EntryId), - Leaf(&'t TokenTree<Span>), - /// End entries contain a pointer to the entry from the containing - /// token tree, or [`None`] if this is the outermost level. - End(Option<EntryPtr>), -} - -/// A token tree buffer -/// The safe version of `syn` [`TokenBuffer`](https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L41) -#[derive(Debug)] -pub struct TokenBuffer<'t, Span> { - buffers: Vec<Box<[Entry<'t, Span>]>>, -} - -trait TokenList<'a, Span> { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree<Span>, Option<&'a TokenTree<Span>>))>, Vec<Entry<'a, Span>>); -} - -impl<'a, Span> TokenList<'a, Span> for &'a [TokenTree<Span>] { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree<Span>, Option<&'a TokenTree<Span>>))>, Vec<Entry<'a, Span>>) - { - // Must contain everything in tokens and then the Entry::End - let start_capacity = self.len() + 1; - let mut entries = Vec::with_capacity(start_capacity); - let mut children = vec![]; - for (idx, tt) in self.iter().enumerate() { - match tt { - TokenTree::Leaf(_) => { - entries.push(Entry::Leaf(tt)); - } - TokenTree::Subtree(subtree) => { - entries.push(Entry::End(None)); - children.push((idx, (subtree, Some(tt)))); - } - } - } - (children, entries) - } -} - -impl<'a, Span> TokenList<'a, Span> for &'a Subtree<Span> { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree<Span>, Option<&'a TokenTree<Span>>))>, Vec<Entry<'a, Span>>) - { - // Must contain everything in tokens and then the Entry::End - let mut entries = vec![]; - let mut children = vec![]; - entries.push(Entry::End(None)); - children.push((0usize, (*self, None))); - (children, entries) - } -} - -impl<'t, Span> TokenBuffer<'t, Span> { - pub fn from_tokens(tokens: &'t [TokenTree<Span>]) -> TokenBuffer<'t, Span> { - Self::new(tokens) - } - - pub fn from_subtree(subtree: &'t Subtree<Span>) -> TokenBuffer<'t, Span> { - Self::new(subtree) - } - - fn new<T: TokenList<'t, Span>>(tokens: T) -> TokenBuffer<'t, Span> { - let mut buffers = vec![]; - let idx = TokenBuffer::new_inner(tokens, &mut buffers, None); - assert_eq!(idx, 0); - TokenBuffer { buffers } - } - - fn new_inner<T: TokenList<'t, Span>>( - tokens: T, - buffers: &mut Vec<Box<[Entry<'t, Span>]>>, - next: Option<EntryPtr>, - ) -> usize { - let (children, mut entries) = tokens.entries(); - - entries.push(Entry::End(next)); - let res = buffers.len(); - buffers.push(entries.into_boxed_slice()); - - for (child_idx, (subtree, tt)) in children { - let idx = TokenBuffer::new_inner( - &*subtree.token_trees, - buffers, - Some(EntryPtr(EntryId(res), child_idx + 1)), - ); - buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, subtree, EntryId(idx)); - } - - res - } - - /// Creates a cursor referencing the first token in the buffer and able to - /// traverse until the end of the buffer. - pub fn begin(&self) -> Cursor<'_, Span> { - Cursor::create(self, EntryPtr(EntryId(0), 0)) - } - - fn entry(&self, ptr: &EntryPtr) -> Option<&Entry<'_, Span>> { - let id = ptr.0; - self.buffers[id.0].get(ptr.1) - } -} - -#[derive(Debug)] -pub enum TokenTreeRef<'a, Span> { - Subtree(&'a Subtree<Span>, Option<&'a TokenTree<Span>>), - Leaf(&'a Leaf<Span>, &'a TokenTree<Span>), -} - -impl<Span: Copy> TokenTreeRef<'_, Span> { - pub fn span(&self) -> Span { - match self { - TokenTreeRef::Subtree(subtree, _) => subtree.delimiter.open, - TokenTreeRef::Leaf(leaf, _) => *leaf.span(), - } - } -} - -impl<Span: Clone> TokenTreeRef<'_, Span> { - pub fn cloned(&self) -> TokenTree<Span> { - match self { - TokenTreeRef::Subtree(subtree, tt) => match tt { - Some(it) => (*it).clone(), - None => (*subtree).clone().into(), - }, - TokenTreeRef::Leaf(_, tt) => (*tt).clone(), - } - } -} - -/// A safe version of `Cursor` from `syn` crate <https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125> -#[derive(Copy, Clone, Debug)] pub struct Cursor<'a, Span> { - buffer: &'a TokenBuffer<'a, Span>, - ptr: EntryPtr, + buffer: &'a [TokenTree<Span>], + index: usize, + subtrees_stack: Vec<usize>, } -impl<Span> PartialEq for Cursor<'_, Span> { - fn eq(&self, other: &Cursor<'_, Span>) -> bool { - self.ptr == other.ptr && std::ptr::eq(self.buffer, other.buffer) +impl<'a, Span: Copy> Cursor<'a, Span> { + pub fn new(buffer: &'a [TokenTree<Span>]) -> Self { + Self { buffer, index: 0, subtrees_stack: Vec::new() } } -} -impl<Span> Eq for Cursor<'_, Span> {} - -impl<'a, Span> Cursor<'a, Span> { /// Check whether it is eof - pub fn eof(self) -> bool { - matches!(self.buffer.entry(&self.ptr), None | Some(Entry::End(None))) + pub fn eof(&self) -> bool { + self.index == self.buffer.len() && self.subtrees_stack.is_empty() } - /// If the cursor is pointing at the end of a subtree, returns - /// the parent subtree - pub fn end(self) -> Option<&'a Subtree<Span>> { - match self.entry() { - Some(Entry::End(Some(ptr))) => { - let idx = ptr.1; - if let Some(Entry::Subtree(_, subtree, _)) = - self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) - { - return Some(subtree); - } - None + pub fn is_root(&self) -> bool { + self.subtrees_stack.is_empty() + } + + fn last_subtree(&self) -> Option<(usize, &'a Subtree<Span>)> { + self.subtrees_stack.last().map(|&subtree_idx| { + let TokenTree::Subtree(subtree) = &self.buffer[subtree_idx] else { + panic!("subtree pointing to non-subtree"); + }; + (subtree_idx, subtree) + }) + } + + pub fn end(&mut self) -> &'a Subtree<Span> { + let (last_subtree_idx, last_subtree) = + self.last_subtree().expect("called `Cursor::end()` without an open subtree"); + // +1 because `Subtree.len` excludes the subtree itself. + assert_eq!( + last_subtree_idx + last_subtree.usize_len() + 1, + self.index, + "called `Cursor::end()` without finishing a subtree" + ); + self.subtrees_stack.pop(); + last_subtree + } + + /// Returns the `TokenTree` at the cursor if it is not at the end of a subtree. + pub fn token_tree(&self) -> Option<&'a TokenTree<Span>> { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { + return None; } - _ => None, } + self.buffer.get(self.index) } - fn entry(&self) -> Option<&'a Entry<'a, Span>> { - self.buffer.entry(&self.ptr) - } - - /// If the cursor is pointing at a `Subtree`, returns - /// a cursor into that subtree - pub fn subtree(self) -> Option<Cursor<'a, Span>> { - match self.entry() { - Some(Entry::Subtree(_, _, entry_id)) => { - Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) - } - _ => None, + /// Bump the cursor, and enters a subtree if it is on one. + pub fn bump(&mut self) { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + assert_ne!( + last_subtree_idx + last_subtree.usize_len() + 1, + self.index, + "called `Cursor::bump()` when at the end of a subtree" + ); } - } - - /// If the cursor is pointing at a `TokenTree`, returns it - pub fn token_tree(self) -> Option<TokenTreeRef<'a, Span>> { - match self.entry() { - Some(Entry::Leaf(tt)) => match tt { - TokenTree::Leaf(leaf) => Some(TokenTreeRef::Leaf(leaf, tt)), - TokenTree::Subtree(subtree) => Some(TokenTreeRef::Subtree(subtree, Some(tt))), - }, - Some(Entry::Subtree(tt, subtree, _)) => Some(TokenTreeRef::Subtree(subtree, *tt)), - Some(Entry::End(_)) | None => None, + if let TokenTree::Subtree(_) = self.buffer[self.index] { + self.subtrees_stack.push(self.index); } + self.index += 1; } - fn create(buffer: &'a TokenBuffer<'_, Span>, ptr: EntryPtr) -> Cursor<'a, Span> { - Cursor { buffer, ptr } - } - - /// Bump the cursor - pub fn bump(self) -> Cursor<'a, Span> { - if let Some(Entry::End(exit)) = self.buffer.entry(&self.ptr) { - match exit { - Some(exit) => Cursor::create(self.buffer, *exit), - None => self, + pub fn bump_or_end(&mut self) { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { + self.subtrees_stack.pop(); + return; } - } else { - Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)) } + // +1 because `Subtree.len` excludes the subtree itself. + if let TokenTree::Subtree(_) = self.buffer[self.index] { + self.subtrees_stack.push(self.index); + } + self.index += 1; } - /// Bump the cursor, if it is a subtree, returns - /// a cursor into that subtree - pub fn bump_subtree(self) -> Cursor<'a, Span> { - match self.entry() { - Some(&Entry::Subtree(_, _, entry_id)) => { - Cursor::create(self.buffer, EntryPtr(entry_id, 0)) + pub fn peek_two_leaves(&self) -> Option<[&'a Leaf<Span>; 2]> { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + let last_end = last_subtree_idx + last_subtree.usize_len() + 1; + if last_end == self.index || last_end == self.index + 1 { + return None; } - Some(Entry::End(exit)) => match exit { - Some(exit) => Cursor::create(self.buffer, *exit), - None => self, - }, - _ => Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)), } + self.buffer.get(self.index..self.index + 2).and_then(|it| match it { + [TokenTree::Leaf(a), TokenTree::Leaf(b)] => Some([a, b]), + _ => None, + }) } - /// Check whether it is a top level - pub fn is_root(&self) -> bool { - let entry_id = self.ptr.0; - entry_id.0 == 0 + pub fn crossed(&self) -> TokenTreesView<'a, Span> { + assert!(self.is_root()); + TokenTreesView::new(&self.buffer[..self.index]) } } diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index 587b903aa97..1d88218810d 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -1,51 +1,64 @@ //! A "Parser" structure for token trees. We use this when parsing a declarative //! macro definition into a list of patterns and templates. +use std::fmt; + use arrayvec::ArrayVec; use intern::sym; -use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree}; +use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct TtIter<'a, S> { inner: std::slice::Iter<'a, TokenTree<S>>, } -impl<'a, S: Copy> TtIter<'a, S> { - pub fn new(subtree: &'a Subtree<S>) -> TtIter<'a, S> { - TtIter { inner: subtree.token_trees.iter() } +impl<S: Copy + fmt::Debug> fmt::Debug for TtIter<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TtIter").field("remaining", &self.remaining()).finish() } +} - pub fn new_iter(iter: std::slice::Iter<'a, TokenTree<S>>) -> TtIter<'a, S> { - TtIter { inner: iter } +#[derive(Clone, Copy)] +pub struct TtIterSavepoint<'a, S>(&'a [TokenTree<S>]); + +impl<'a, S: Copy> TtIterSavepoint<'a, S> { + pub fn remaining(self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.0) + } +} + +impl<'a, S: Copy> TtIter<'a, S> { + pub(crate) fn new(tt: &'a [TokenTree<S>]) -> TtIter<'a, S> { + TtIter { inner: tt.iter() } } pub fn expect_char(&mut self, char: char) -> Result<(), ()> { match self.next() { - Some(&TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()), + Some(TtElement::Leaf(&Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()), _ => Err(()), } } pub fn expect_any_char(&mut self, chars: &[char]) -> Result<(), ()> { match self.next() { - Some(TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => { + Some(TtElement::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => { Ok(()) } _ => Err(()), } } - pub fn expect_subtree(&mut self) -> Result<&'a Subtree<S>, ()> { + pub fn expect_subtree(&mut self) -> Result<(&'a Subtree<S>, TtIter<'a, S>), ()> { match self.next() { - Some(TokenTree::Subtree(it)) => Ok(it), + Some(TtElement::Subtree(subtree, iter)) => Ok((subtree, iter)), _ => Err(()), } } pub fn expect_leaf(&mut self) -> Result<&'a Leaf<S>, ()> { match self.next() { - Some(TokenTree::Leaf(it)) => Ok(it), + Some(TtElement::Leaf(it)) => Ok(it), _ => Err(()), } } @@ -99,7 +112,7 @@ impl<'a, S: Copy> TtIter<'a, S> { /// This method currently may return a single quotation, which is part of lifetime ident and /// conceptually not a punct in the context of mbe. Callers should handle this. pub fn expect_glued_punct(&mut self) -> Result<ArrayVec<Punct<S>, 3>, ()> { - let TokenTree::Leaf(Leaf::Punct(first)) = self.next().ok_or(())?.clone() else { + let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else { return Err(()); }; @@ -132,6 +145,7 @@ impl<'a, S: Copy> TtIter<'a, S> { } ('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _) | ('-' | '=' | '>', '>', _) + | (_, _, Some(';')) | ('<', '-', _) | (':', ':', _) | ('.', '.', _) @@ -146,28 +160,84 @@ impl<'a, S: Copy> TtIter<'a, S> { } Ok(res) } - pub fn peek_n(&self, n: usize) -> Option<&'a TokenTree<S>> { + + /// This method won't check for subtrees, so the nth token tree may not be the nth sibling of the current tree. + fn peek_n(&self, n: usize) -> Option<&'a TokenTree<S>> { self.inner.as_slice().get(n) } + pub fn peek(&self) -> Option<TtElement<'a, S>> { + match self.inner.as_slice().first()? { + TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), + TokenTree::Subtree(subtree) => { + let nested_iter = + TtIter { inner: self.inner.as_slice()[1..][..subtree.usize_len()].iter() }; + Some(TtElement::Subtree(subtree, nested_iter)) + } + } + } + + /// Equivalent to `peek().is_none()`, but a bit faster. + pub fn is_empty(&self) -> bool { + self.inner.len() == 0 + } + pub fn next_span(&self) -> Option<S> { Some(self.inner.as_slice().first()?.first_span()) } - pub fn as_slice(&self) -> &'a [TokenTree<S>] { - self.inner.as_slice() + pub fn remaining(&self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.inner.as_slice()) } -} -impl<'a, S> Iterator for TtIter<'a, S> { - type Item = &'a TokenTree<S>; - fn next(&mut self) -> Option<Self::Item> { - self.inner.next() + /// **Warning**: This advances `skip` **flat** token trees, subtrees account for children+1! + pub fn flat_advance(&mut self, skip: usize) { + self.inner = self.inner.as_slice()[skip..].iter(); + } + + pub fn savepoint(&self) -> TtIterSavepoint<'a, S> { + TtIterSavepoint(self.inner.as_slice()) + } + + pub fn from_savepoint(&self, savepoint: TtIterSavepoint<'a, S>) -> TokenTreesView<'a, S> { + let len = (self.inner.as_slice().as_ptr() as usize - savepoint.0.as_ptr() as usize) + / size_of::<TokenTree<S>>(); + TokenTreesView::new(&savepoint.0[..len]) } - fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() + pub fn next_as_view(&mut self) -> Option<TokenTreesView<'a, S>> { + let savepoint = self.savepoint(); + self.next()?; + Some(self.from_savepoint(savepoint)) } } -impl<S> std::iter::ExactSizeIterator for TtIter<'_, S> {} +pub enum TtElement<'a, S> { + Leaf(&'a Leaf<S>), + Subtree(&'a Subtree<S>, TtIter<'a, S>), +} + +impl<S: Copy> TtElement<'_, S> { + #[inline] + pub fn first_span(&self) -> S { + match self { + TtElement::Leaf(it) => *it.span(), + TtElement::Subtree(it, _) => it.delimiter.open, + } + } +} + +impl<'a, S> Iterator for TtIter<'a, S> { + type Item = TtElement<'a, S>; + fn next(&mut self) -> Option<Self::Item> { + match self.inner.next()? { + TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), + TokenTree::Subtree(subtree) => { + let nested_iter = + TtIter { inner: self.inner.as_slice()[..subtree.usize_len()].iter() }; + self.inner = self.inner.as_slice()[subtree.usize_len()..].iter(); + Some(TtElement::Subtree(subtree, nested_iter)) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 8d915d0a51e..7705ba876e1 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -1,6 +1,7 @@ //! `tt` crate defines a `TokenTree` data structure: this is the interface (both -//! input and output) of macros. It closely mirrors `proc_macro` crate's -//! `TokenTree`. +//! input and output) of macros. +//! +//! The `TokenTree` is semantically a tree, but for performance reasons it is stored as a flat structure. #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] @@ -14,7 +15,9 @@ pub mod iter; use std::fmt; +use buffer::Cursor; use intern::Symbol; +use iter::{TtElement, TtIter}; use stdx::{impl_from, itertools::Itertools as _}; pub use text_size::{TextRange, TextSize}; @@ -75,23 +78,6 @@ pub enum TokenTree<S = u32> { } impl_from!(Leaf<S>, Subtree<S> for TokenTree); impl<S: Copy> TokenTree<S> { - pub fn empty(span: S) -> Self { - Self::Subtree(Subtree { - delimiter: Delimiter::invisible_spanned(span), - token_trees: Box::new([]), - }) - } - - pub fn subtree_or_wrap(self, span: DelimSpan<S>) -> Subtree<S> { - match self { - TokenTree::Leaf(_) => Subtree { - delimiter: Delimiter::invisible_delim_spanned(span), - token_trees: Box::new([self]), - }, - TokenTree::Subtree(s) => s, - } - } - pub fn first_span(&self) -> S { match self { TokenTree::Leaf(l) => *l.span(), @@ -118,38 +104,422 @@ impl<S> Leaf<S> { } impl_from!(Literal<S>, Punct<S>, Ident<S> for Leaf); -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Subtree<S> { pub delimiter: Delimiter<S>, - pub token_trees: Box<[TokenTree<S>]>, + /// Number of following token trees that belong to this subtree, excluding this subtree. + pub len: u32, } -impl<S: Copy> Subtree<S> { +impl<S> Subtree<S> { + pub fn usize_len(&self) -> usize { + self.len as usize + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct TopSubtree<S>(pub Box<[TokenTree<S>]>); + +impl<S: Copy> TopSubtree<S> { pub fn empty(span: DelimSpan<S>) -> Self { - Subtree { delimiter: Delimiter::invisible_delim_spanned(span), token_trees: Box::new([]) } + Self(Box::new([TokenTree::Subtree(Subtree { + delimiter: Delimiter::invisible_delim_spanned(span), + len: 0, + })])) + } + + pub fn invisible_from_leaves<const N: usize>(delim_span: S, leaves: [Leaf<S>; N]) -> Self { + let mut builder = TopSubtreeBuilder::new(Delimiter::invisible_spanned(delim_span)); + builder.extend(leaves); + builder.build() + } + + pub fn from_token_trees(delimiter: Delimiter<S>, token_trees: TokenTreesView<'_, S>) -> Self { + let mut builder = TopSubtreeBuilder::new(delimiter); + builder.extend_with_tt(token_trees); + builder.build() + } + + pub fn from_subtree(subtree: SubtreeView<'_, S>) -> Self { + Self(subtree.0.into()) + } + + pub fn view(&self) -> SubtreeView<'_, S> { + SubtreeView::new(&self.0) + } + + pub fn iter(&self) -> TtIter<'_, S> { + self.view().iter() } - /// This is slow, and should be avoided, as it will always reallocate! - pub fn push(&mut self, subtree: TokenTree<S>) { - let mut mutable_trees = std::mem::take(&mut self.token_trees).into_vec(); + pub fn top_subtree(&self) -> &Subtree<S> { + self.view().top_subtree() + } - // Reserve exactly space for one element, to avoid `into_boxed_slice` having to reallocate again. - mutable_trees.reserve_exact(1); - mutable_trees.push(subtree); + pub fn top_subtree_delimiter_mut(&mut self) -> &mut Delimiter<S> { + let TokenTree::Subtree(subtree) = &mut self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + &mut subtree.delimiter + } - self.token_trees = mutable_trees.into_boxed_slice(); + pub fn token_trees(&self) -> TokenTreesView<'_, S> { + self.view().token_trees() } } -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct SubtreeBuilder<S> { - pub delimiter: Delimiter<S>, - pub token_trees: Vec<TokenTree<S>>, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TopSubtreeBuilder<S> { + unclosed_subtree_indices: Vec<usize>, + token_trees: Vec<TokenTree<S>>, + last_closed_subtree: Option<usize>, +} + +impl<S: Copy> TopSubtreeBuilder<S> { + pub fn new(top_delimiter: Delimiter<S>) -> Self { + let mut result = Self { + unclosed_subtree_indices: Vec::new(), + token_trees: Vec::new(), + last_closed_subtree: None, + }; + let top_subtree = TokenTree::Subtree(Subtree { delimiter: top_delimiter, len: 0 }); + result.token_trees.push(top_subtree); + result + } + + pub fn open(&mut self, delimiter_kind: DelimiterKind, open_span: S) { + self.unclosed_subtree_indices.push(self.token_trees.len()); + self.token_trees.push(TokenTree::Subtree(Subtree { + delimiter: Delimiter { + open: open_span, + close: open_span, // Will be overwritten on close. + kind: delimiter_kind, + }, + len: 0, + })); + } + + pub fn close(&mut self, close_span: S) { + let last_unclosed_index = self + .unclosed_subtree_indices + .pop() + .expect("attempt to close a `tt::Subtree` when none is open"); + let subtree_len = (self.token_trees.len() - last_unclosed_index - 1) as u32; + let TokenTree::Subtree(subtree) = &mut self.token_trees[last_unclosed_index] else { + unreachable!("unclosed token tree is always a subtree"); + }; + subtree.len = subtree_len; + subtree.delimiter.close = close_span; + self.last_closed_subtree = Some(last_unclosed_index); + } + + /// You cannot call this consecutively, it will only work once after close. + pub fn remove_last_subtree_if_invisible(&mut self) { + let Some(last_subtree_idx) = self.last_closed_subtree else { return }; + if let TokenTree::Subtree(Subtree { + delimiter: Delimiter { kind: DelimiterKind::Invisible, .. }, + .. + }) = self.token_trees[last_subtree_idx] + { + self.token_trees.remove(last_subtree_idx); + self.last_closed_subtree = None; + } + } + + pub fn push(&mut self, leaf: Leaf<S>) { + self.token_trees.push(TokenTree::Leaf(leaf)); + } + + pub fn extend(&mut self, leaves: impl IntoIterator<Item = Leaf<S>>) { + self.token_trees.extend(leaves.into_iter().map(TokenTree::Leaf)); + } + + /// This does not check the token trees are valid, beware! + pub fn extend_tt_dangerous(&mut self, tt: impl IntoIterator<Item = TokenTree<S>>) { + self.token_trees.extend(tt); + } + + pub fn extend_with_tt(&mut self, tt: TokenTreesView<'_, S>) { + self.token_trees.extend(tt.0.iter().cloned()); + } + + pub fn expected_delimiter(&self) -> Option<&Delimiter<S>> { + self.unclosed_subtree_indices.last().map(|&subtree_idx| { + let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { + unreachable!("unclosed token tree is always a subtree") + }; + &subtree.delimiter + }) + } + + /// Converts unclosed subtree to a punct of their open delimiter. + // FIXME: This is incorrect to do, delimiters can never be puncts. See #18244. + pub fn flatten_unclosed_subtrees(&mut self) { + for &subtree_idx in &self.unclosed_subtree_indices { + let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { + unreachable!("unclosed token tree is always a subtree") + }; + let char = match subtree.delimiter.kind { + DelimiterKind::Parenthesis => '(', + DelimiterKind::Brace => '{', + DelimiterKind::Bracket => '[', + DelimiterKind::Invisible => '$', + }; + self.token_trees[subtree_idx] = TokenTree::Leaf(Leaf::Punct(Punct { + char, + spacing: Spacing::Alone, + span: subtree.delimiter.open, + })); + } + self.unclosed_subtree_indices.clear(); + } + + /// Builds, and remove the top subtree if it has only one subtree child. + pub fn build_skip_top_subtree(mut self) -> TopSubtree<S> { + let top_tts = TokenTreesView::new(&self.token_trees[1..]); + match top_tts.try_into_subtree() { + Some(_) => { + assert!( + self.unclosed_subtree_indices.is_empty(), + "attempt to build an unbalanced `TopSubtreeBuilder`" + ); + TopSubtree(self.token_trees.drain(1..).collect()) + } + None => self.build(), + } + } + + pub fn build(mut self) -> TopSubtree<S> { + assert!( + self.unclosed_subtree_indices.is_empty(), + "attempt to build an unbalanced `TopSubtreeBuilder`" + ); + let total_len = self.token_trees.len() as u32; + let TokenTree::Subtree(top_subtree) = &mut self.token_trees[0] else { + unreachable!("first token tree is always a subtree"); + }; + top_subtree.len = total_len - 1; + TopSubtree(self.token_trees.into_boxed_slice()) + } + + pub fn restore_point(&self) -> SubtreeBuilderRestorePoint { + SubtreeBuilderRestorePoint { + unclosed_subtree_indices_len: self.unclosed_subtree_indices.len(), + token_trees_len: self.token_trees.len(), + last_closed_subtree: self.last_closed_subtree, + } + } + + pub fn restore(&mut self, restore_point: SubtreeBuilderRestorePoint) { + self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len); + self.token_trees.truncate(restore_point.token_trees_len); + self.last_closed_subtree = restore_point.last_closed_subtree; + } +} + +#[derive(Clone, Copy)] +pub struct SubtreeBuilderRestorePoint { + unclosed_subtree_indices_len: usize, + token_trees_len: usize, + last_closed_subtree: Option<usize>, +} + +#[derive(Clone, Copy)] +pub struct TokenTreesView<'a, S>(&'a [TokenTree<S>]); + +impl<'a, S: Copy> TokenTreesView<'a, S> { + pub fn new(tts: &'a [TokenTree<S>]) -> Self { + if cfg!(debug_assertions) { + tts.iter().enumerate().for_each(|(idx, tt)| { + if let TokenTree::Subtree(tt) = &tt { + // `<` and not `<=` because `Subtree.len` does not include the subtree node itself. + debug_assert!( + idx + tt.usize_len() < tts.len(), + "`TokenTreeView::new()` was given a cut-in-half list" + ); + } + }); + } + Self(tts) + } + + pub fn iter(&self) -> TtIter<'a, S> { + TtIter::new(self.0) + } + + pub fn cursor(&self) -> Cursor<'a, S> { + Cursor::new(self.0) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn try_into_subtree(self) -> Option<SubtreeView<'a, S>> { + if let Some(TokenTree::Subtree(subtree)) = self.0.first() { + if subtree.usize_len() == (self.0.len() - 1) { + return Some(SubtreeView::new(self.0)); + } + } + None + } + + pub fn strip_invisible(self) -> TokenTreesView<'a, S> { + self.try_into_subtree().map(|subtree| subtree.strip_invisible()).unwrap_or(self) + } + + /// This returns a **flat** structure of tokens (subtrees will be represented by a single node + /// preceding their children), so it isn't suited for most use cases, only for matching leaves + /// at the beginning/end with no subtrees before them. If you need a structured pass, use [`TtIter`]. + pub fn flat_tokens(&self) -> &'a [TokenTree<S>] { + self.0 + } + + pub fn split( + self, + mut split_fn: impl FnMut(TtElement<'a, S>) -> bool, + ) -> impl Iterator<Item = TokenTreesView<'a, S>> { + let mut subtree_iter = self.iter(); + let mut need_to_yield_even_if_empty = true; + let result = std::iter::from_fn(move || { + if subtree_iter.is_empty() && !need_to_yield_even_if_empty { + return None; + }; + + need_to_yield_even_if_empty = false; + let savepoint = subtree_iter.savepoint(); + let mut result = subtree_iter.from_savepoint(savepoint); + while let Some(tt) = subtree_iter.next() { + if split_fn(tt) { + need_to_yield_even_if_empty = true; + break; + } + result = subtree_iter.from_savepoint(savepoint); + } + Some(result) + }); + result + } } -impl<S> SubtreeBuilder<S> { - pub fn build(self) -> Subtree<S> { - Subtree { delimiter: self.delimiter, token_trees: self.token_trees.into_boxed_slice() } +impl<S: fmt::Debug + Copy> fmt::Debug for TokenTreesView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut iter = self.iter(); + while let Some(tt) = iter.next() { + print_debug_token(f, 0, tt)?; + if !iter.is_empty() { + writeln!(f)?; + } + } + Ok(()) + } +} + +impl<S: Copy> fmt::Display for TokenTreesView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + return token_trees_display(f, self.iter()); + + fn subtree_display<S>( + subtree: &Subtree<S>, + f: &mut fmt::Formatter<'_>, + iter: TtIter<'_, S>, + ) -> fmt::Result { + let (l, r) = match subtree.delimiter.kind { + DelimiterKind::Parenthesis => ("(", ")"), + DelimiterKind::Brace => ("{", "}"), + DelimiterKind::Bracket => ("[", "]"), + DelimiterKind::Invisible => ("", ""), + }; + f.write_str(l)?; + token_trees_display(f, iter)?; + f.write_str(r)?; + Ok(()) + } + + fn token_trees_display<S>(f: &mut fmt::Formatter<'_>, iter: TtIter<'_, S>) -> fmt::Result { + let mut needs_space = false; + for child in iter { + if needs_space { + f.write_str(" ")?; + } + needs_space = true; + + match child { + TtElement::Leaf(Leaf::Punct(p)) => { + needs_space = p.spacing == Spacing::Alone; + fmt::Display::fmt(p, f)?; + } + TtElement::Leaf(leaf) => fmt::Display::fmt(leaf, f)?, + TtElement::Subtree(subtree, subtree_iter) => { + subtree_display(subtree, f, subtree_iter)? + } + } + } + Ok(()) + } + } +} + +#[derive(Clone, Copy)] +// Invariant: always starts with `Subtree` that covers the entire thing. +pub struct SubtreeView<'a, S>(&'a [TokenTree<S>]); + +impl<'a, S: Copy> SubtreeView<'a, S> { + pub fn new(tts: &'a [TokenTree<S>]) -> Self { + if cfg!(debug_assertions) { + let TokenTree::Subtree(subtree) = &tts[0] else { + panic!("first token tree must be a subtree in `SubtreeView`"); + }; + assert_eq!( + subtree.usize_len(), + tts.len() - 1, + "subtree must cover the entire `SubtreeView`" + ); + } + Self(tts) + } + + pub fn as_token_trees(self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.0) + } + + pub fn iter(&self) -> TtIter<'a, S> { + TtIter::new(&self.0[1..]) + } + + pub fn top_subtree(&self) -> &'a Subtree<S> { + let TokenTree::Subtree(subtree) = &self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + subtree + } + + pub fn strip_invisible(&self) -> TokenTreesView<'a, S> { + if self.top_subtree().delimiter.kind == DelimiterKind::Invisible { + TokenTreesView::new(&self.0[1..]) + } else { + TokenTreesView::new(self.0) + } + } + + pub fn token_trees(&self) -> TokenTreesView<'a, S> { + TokenTreesView::new(&self.0[1..]) + } +} + +impl<S: fmt::Debug + Copy> fmt::Debug for SubtreeView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&TokenTreesView(self.0), f) + } +} + +impl<S: Copy> fmt::Display for SubtreeView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&TokenTreesView(self.0), f) } } @@ -348,6 +718,7 @@ fn print_debug_subtree<S: fmt::Debug>( f: &mut fmt::Formatter<'_>, subtree: &Subtree<S>, level: usize, + iter: TtIter<'_, S>, ) -> fmt::Result { let align = " ".repeat(level); @@ -363,14 +734,9 @@ fn print_debug_subtree<S: fmt::Debug>( fmt::Debug::fmt(&open, f)?; write!(f, " ")?; fmt::Debug::fmt(&close, f)?; - if !subtree.token_trees.is_empty() { + for child in iter { writeln!(f)?; - for (idx, child) in subtree.token_trees.iter().enumerate() { - print_debug_token(f, child, level + 1)?; - if idx != subtree.token_trees.len() - 1 { - writeln!(f)?; - } - } + print_debug_token(f, level + 1, child)?; } Ok(()) @@ -378,13 +744,13 @@ fn print_debug_subtree<S: fmt::Debug>( fn print_debug_token<S: fmt::Debug>( f: &mut fmt::Formatter<'_>, - tkn: &TokenTree<S>, level: usize, + tt: TtElement<'_, S>, ) -> fmt::Result { let align = " ".repeat(level); - match tkn { - TokenTree::Leaf(leaf) => match leaf { + match tt { + TtElement::Leaf(leaf) => match leaf { Leaf::Literal(lit) => { write!( f, @@ -417,54 +783,23 @@ fn print_debug_token<S: fmt::Debug>( )?; } }, - TokenTree::Subtree(subtree) => { - print_debug_subtree(f, subtree, level)?; + TtElement::Subtree(subtree, subtree_iter) => { + print_debug_subtree(f, subtree, level, subtree_iter)?; } } Ok(()) } -impl<S: fmt::Debug> fmt::Debug for Subtree<S> { +impl<S: fmt::Debug + Copy> fmt::Debug for TopSubtree<S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - print_debug_subtree(f, self, 0) + fmt::Debug::fmt(&self.view(), f) } } -impl<S> fmt::Display for TokenTree<S> { +impl<S: fmt::Display + Copy> fmt::Display for TopSubtree<S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TokenTree::Leaf(it) => fmt::Display::fmt(it, f), - TokenTree::Subtree(it) => fmt::Display::fmt(it, f), - } - } -} - -impl<S> fmt::Display for Subtree<S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (l, r) = match self.delimiter.kind { - DelimiterKind::Parenthesis => ("(", ")"), - DelimiterKind::Brace => ("{", "}"), - DelimiterKind::Bracket => ("[", "]"), - DelimiterKind::Invisible => ("", ""), - }; - f.write_str(l)?; - let mut needs_space = false; - for tt in self.token_trees.iter() { - if needs_space { - f.write_str(" ")?; - } - needs_space = true; - match tt { - TokenTree::Leaf(Leaf::Punct(p)) => { - needs_space = p.spacing == Spacing::Alone; - fmt::Display::fmt(p, f)?; - } - tt => fmt::Display::fmt(tt, f)?, - } - } - f.write_str(r)?; - Ok(()) + fmt::Display::fmt(&self.view(), f) } } @@ -538,34 +873,45 @@ impl<S> fmt::Display for Punct<S> { impl<S> Subtree<S> { /// Count the number of tokens recursively pub fn count(&self) -> usize { - let children_count = self - .token_trees - .iter() - .map(|c| match c { - TokenTree::Subtree(c) => c.count(), - TokenTree::Leaf(_) => 0, - }) - .sum::<usize>(); - - self.token_trees.len() + children_count + self.usize_len() } } -impl<S> Subtree<S> { +impl<S> TopSubtree<S> { /// A simple line string used for debugging - pub fn as_debug_string(&self) -> String { - let delim = match self.delimiter.kind { - DelimiterKind::Brace => ("{", "}"), - DelimiterKind::Bracket => ("[", "]"), - DelimiterKind::Parenthesis => ("(", ")"), - DelimiterKind::Invisible => ("$", "$"), - }; + pub fn subtree_as_debug_string(&self, subtree_idx: usize) -> String { + fn debug_subtree<S>( + output: &mut String, + subtree: &Subtree<S>, + iter: &mut std::slice::Iter<'_, TokenTree<S>>, + ) { + let delim = match subtree.delimiter.kind { + DelimiterKind::Brace => ("{", "}"), + DelimiterKind::Bracket => ("[", "]"), + DelimiterKind::Parenthesis => ("(", ")"), + DelimiterKind::Invisible => ("$", "$"), + }; - let mut res = String::new(); - res.push_str(delim.0); - let mut last = None; - for child in self.token_trees.iter() { - let s = match child { + output.push_str(delim.0); + let mut last = None; + let mut idx = 0; + while idx < subtree.len { + let child = iter.next().unwrap(); + debug_token_tree(output, child, last, iter); + last = Some(child); + idx += 1; + } + + output.push_str(delim.1); + } + + fn debug_token_tree<S>( + output: &mut String, + tt: &TokenTree<S>, + last: Option<&TokenTree<S>>, + iter: &mut std::slice::Iter<'_, TokenTree<S>>, + ) { + match tt { TokenTree::Leaf(it) => { let s = match it { Leaf::Literal(it) => it.symbol.to_string(), @@ -574,31 +920,37 @@ impl<S> Subtree<S> { }; match (it, last) { (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => { - " ".to_owned() + &s + output.push(' '); + output.push_str(&s); } (Leaf::Punct(_), Some(TokenTree::Leaf(Leaf::Punct(punct)))) => { if punct.spacing == Spacing::Alone { - " ".to_owned() + &s + output.push(' '); + output.push_str(&s); } else { - s + output.push_str(&s); } } - _ => s, + _ => output.push_str(&s), } } - TokenTree::Subtree(it) => it.as_debug_string(), - }; - res.push_str(&s); - last = Some(child); + TokenTree::Subtree(it) => debug_subtree(output, it, iter), + } } - res.push_str(delim.1); + let mut res = String::new(); + debug_token_tree( + &mut res, + &self.0[subtree_idx], + None, + &mut self.0[subtree_idx + 1..].iter(), + ); res } } -pub fn pretty<S>(tkns: &[TokenTree<S>]) -> String { - fn tokentree_to_text<S>(tkn: &TokenTree<S>) -> String { +pub fn pretty<S>(mut tkns: &[TokenTree<S>]) -> String { + fn tokentree_to_text<S>(tkn: &TokenTree<S>, tkns: &mut &[TokenTree<S>]) -> String { match tkn { TokenTree::Leaf(Leaf::Ident(ident)) => { format!("{}{}", ident.is_raw.as_str(), ident.sym) @@ -606,7 +958,9 @@ pub fn pretty<S>(tkns: &[TokenTree<S>]) -> String { TokenTree::Leaf(Leaf::Literal(literal)) => format!("{literal}"), TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char), TokenTree::Subtree(subtree) => { - let content = pretty(&subtree.token_trees); + let (subtree_content, rest) = tkns.split_at(subtree.usize_len()); + let content = pretty(subtree_content); + *tkns = rest; let (open, close) = match subtree.delimiter.kind { DelimiterKind::Brace => ("{", "}"), DelimiterKind::Bracket => ("[", "]"), @@ -618,16 +972,18 @@ pub fn pretty<S>(tkns: &[TokenTree<S>]) -> String { } } - tkns.iter() - .fold((String::new(), true), |(last, last_to_joint), tkn| { - let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { "" } else { " " }); - let mut is_joint = false; - if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn { - if punct.spacing == Spacing::Joint { - is_joint = true; - } + let mut last = String::new(); + let mut last_to_joint = true; + + while let Some((tkn, rest)) = tkns.split_first() { + tkns = rest; + last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { "" } else { " " }); + last_to_joint = false; + if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn { + if punct.spacing == Spacing::Joint { + last_to_joint = true; } - (s, is_joint) - }) - .0 + } + } + last } diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 0e37611a549..21ac3a5a269 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ <!--- -lsp/ext.rs hash: 9790509d87670c22 +lsp/ext.rs hash: 6dd762ae19630ec0 If you need to change the above hash to make the test pass, please check if you need to adjust this doc as well and ping this issue: diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 142aa22a704..5b86766aa8e 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -135,12 +135,6 @@ Unsetting this disables sysroot loading. This option does not take effect until rust-analyzer is restarted. -- -[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `"cargo_metadata"`):: -+ --- -How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer -to analyze third-party dependencies of the standard libraries. --- [[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: + -- @@ -152,7 +146,7 @@ This option does not take effect until rust-analyzer is restarted. [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: + -- -Compilation target override (target triple). +Compilation target override (target tuple). -- [[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`):: + @@ -286,6 +280,35 @@ In `match` arms it completes a comma instead. Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. -- +[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude:: ++ +-- +Default: +---- +[ + { + "path": "core::borrow::Borrow", + "type": "methods" + }, + { + "path": "core::borrow::BorrowMut", + "type": "methods" + } +] +---- +A list of full paths to items to exclude from auto-importing completions. + +Traits in this list won't have their methods suggested in completions unless the trait +is in scope. + +You can either specify a string path which defaults to type "always" or use the more verbose +form `{ "path": "path::to::item", type: "always" }`. + +For traits the type "methods" can be used to only exclude the methods but not the trait itself. + +This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + +-- [[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: + -- @@ -297,6 +320,15 @@ with `self` prefixed to them when inside a method. -- Whether to add parenthesis and argument snippets when completing function. -- +[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`):: ++ +-- +A list of full paths to traits whose methods to exclude from completion. + +Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + +Note that the trait themselves can still be completed. +-- [[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`):: + -- @@ -497,6 +529,12 @@ Whether to show `References` action. Only applies when Whether to show `Run` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. -- +[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`):: ++ +-- +Whether to show `Update Test` action. Only applies when +`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. +-- [[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`):: + -- @@ -513,6 +551,15 @@ Whether to show keyword hover popups. Only applies when -- Use markdown syntax for links on hover. -- +[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`):: ++ +-- +Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + +This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + +The default is 20 characters. +-- [[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: + -- @@ -799,6 +846,12 @@ Only applies when `#rust-analyzer.lens.enable#` is set. Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. -- +[[rust-analyzer.lens.updateTest.enable]]rust-analyzer.lens.updateTest.enable (default: `true`):: ++ +-- +Whether to show `Update Test` lens. Only applies when +`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. +-- [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: + -- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index da2aa4eae4e..ffc820e9b7f 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -769,7 +769,7 @@ interface Crate { /// The set of cfgs activated for a given crate, like /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. cfg: string[]; - /// Target triple for this Crate. + /// Target tuple for this Crate. /// /// Used when running `rustc --print cfg` /// to get target-specific cfgs. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index df97efaae73..80246bf3fea 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -407,6 +407,11 @@ "$rustc" ], "markdownDescription": "Problem matchers to use for `rust-analyzer.run` command, eg `[\"$rustc\", \"$rust-panic\"]`." + }, + "rust-analyzer.runnables.askBeforeUpdateTest": { + "type": "boolean", + "default": true, + "markdownDescription": "Ask before updating the test when running it." } } }, @@ -426,40 +431,55 @@ "default": "openLogs", "markdownDescription": "Action to run when clicking the extension status bar item." }, - "rust-analyzer.statusBar.documentSelector": { - "type": [ - "array", - "null" - ], - "items": { - "type": "object", - "properties": { - "language": { - "type": [ - "string", - "null" - ] - }, - "pattern": { - "type": [ - "string", - "null" - ] - } - } - }, - "default": [ - { - "language": "rust" - }, + "rust-analyzer.statusBar.showStatusBar": { + "markdownDescription": "When to show the extension status bar.\n\n`\"always\"` Always show the status bar.\n\n`\"never\"` Never show the status bar.\n\n`{ documentSelector: <DocumentSelector>[] }` Show the status bar if the open file matches any of the given document selectors.\n\nSee [VS Code -- DocumentSelector](https://code.visualstudio.com/api/references/document-selector) for more information.", + "anyOf": [ { - "pattern": "**/Cargo.toml" + "type": "string", + "enum": [ + "always", + "never" + ] }, { - "pattern": "**/Cargo.lock" + "type": "object", + "properties": { + "documentSelector": { + "type": "array", + "items": { + "type": "object", + "properties": { + "language": { + "type": "string" + }, + "notebookType": { + "type": "string" + }, + "scheme": { + "type": "string" + }, + "pattern": { + "type": "string" + } + } + } + } + } } ], - "markdownDescription": "Determines when to show the extension status bar item based on the currently open file. Use `{ \"pattern\": \"**\" }` to always show. Use `null` to never show." + "default": { + "documentSelector": [ + { + "language": "rust" + }, + { + "pattern": "**/Cargo.toml" + }, + { + "pattern": "**/Cargo.lock" + } + ] + } } } }, @@ -854,24 +874,6 @@ { "title": "cargo", "properties": { - "rust-analyzer.cargo.sysrootQueryMetadata": { - "markdownDescription": "How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer\nto analyze third-party dependencies of the standard libraries.", - "default": "cargo_metadata", - "type": "string", - "enum": [ - "none", - "cargo_metadata" - ], - "enumDescriptions": [ - "Do not query sysroot metadata, always use stitched sysroot.", - "Use `cargo metadata` to query sysroot metadata." - ] - } - } - }, - { - "title": "cargo", - "properties": { "rust-analyzer.cargo.sysrootSrc": { "markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.", "default": null, @@ -886,7 +888,7 @@ "title": "cargo", "properties": { "rust-analyzer.cargo.target": { - "markdownDescription": "Compilation target override (target triple).", + "markdownDescription": "Compilation target override (target tuple).", "default": null, "type": [ "null", @@ -1127,6 +1129,51 @@ { "title": "completion", "properties": { + "rust-analyzer.completion.autoimport.exclude": { + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more verbose\nform `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait itself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "default": [ + { + "path": "core::borrow::Borrow", + "type": "methods" + }, + { + "path": "core::borrow::BorrowMut", + "type": "methods" + } + ], + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "always", + "methods" + ], + "enumDescriptions": [ + "Do not show this item or its methods (if it is a trait) in auto-import completions.", + "Do not show this traits methods in auto-import completions." + ] + } + } + } + ] + } + } + } + }, + { + "title": "completion", + "properties": { "rust-analyzer.completion.autoself.enable": { "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.", "default": true, @@ -1157,6 +1204,19 @@ { "title": "completion", "properties": { + "rust-analyzer.completion.excludeTraits": { + "markdownDescription": "A list of full paths to traits whose methods to exclude from completion.\n\nMethods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.\n\nNote that the trait themselves can still be completed.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "title": "completion", + "properties": { "rust-analyzer.completion.fullFunctionSignatures.enable": { "markdownDescription": "Whether to show full function/method signatures in completion docs.", "default": false, @@ -1503,6 +1563,16 @@ { "title": "hover", "properties": { + "rust-analyzer.hover.actions.updateTest.enable": { + "markdownDescription": "Whether to show `Update Test` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "hover", + "properties": { "rust-analyzer.hover.documentation.enable": { "markdownDescription": "Whether to show documentation on hover.", "default": true, @@ -1533,6 +1603,29 @@ { "title": "hover", "properties": { + "rust-analyzer.hover.maxSubstitutionLength": { + "markdownDescription": "Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.\n\nThis can take three values: `null` means \"unlimited\", the string `\"hide\"` means to not show generic substitutions at all, and a number means to limit them to X characters.\n\nThe default is 20 characters.", + "default": 20, + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "hide" + ] + }, + { + "type": "integer" + } + ] + } + } + }, + { + "title": "hover", + "properties": { "rust-analyzer.hover.memoryLayout.alignment": { "markdownDescription": "How to render the align information in a memory layout hover.", "default": "hexadecimal", @@ -2258,6 +2351,16 @@ } }, { + "title": "lens", + "properties": { + "rust-analyzer.lens.updateTest.enable": { + "markdownDescription": "Whether to show `Update Test` lens. Only applies when\n`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.", + "default": true, + "type": "boolean" + } + } + }, + { "title": "general", "properties": { "rust-analyzer.linkedProjects": { diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 7ebc186a3ea..73e39c900e7 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -1139,11 +1139,37 @@ export function peekTests(ctx: CtxInit): Cmd { }; } +function isUpdatingTest(runnable: ra.Runnable): boolean { + if (!isCargoRunnableArgs(runnable.args)) { + return false; + } + + const env = runnable.args.environment; + return env ? ["UPDATE_EXPECT", "INSTA_UPDATE", "SNAPSHOTS"].some((key) => key in env) : false; +} + export function runSingle(ctx: CtxInit): Cmd { return async (runnable: ra.Runnable) => { const editor = ctx.activeRustEditor; if (!editor) return; + if (isUpdatingTest(runnable) && ctx.config.askBeforeUpdateTest) { + const selection = await vscode.window.showInformationMessage( + "rust-analyzer", + { detail: "Do you want to update tests?", modal: true }, + "Update Now", + "Update (and Don't ask again)", + ); + + if (selection !== "Update Now" && selection !== "Update (and Don't ask again)") { + return; + } + + if (selection === "Update (and Don't ask again)") { + await ctx.config.setAskBeforeUpdateTest(false); + } + } + const task = await createTaskFromRunnable(runnable, ctx.config); task.group = vscode.TaskGroup.Build; task.presentationOptions = { diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index f7ef80df2ba..720c473c5b4 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -13,6 +13,13 @@ export type RunnableEnvCfgItem = { }; export type RunnableEnvCfg = Record<string, string> | RunnableEnvCfgItem[]; +type ShowStatusBar = + | "always" + | "never" + | { + documentSelector: vscode.DocumentSelector; + }; + export class Config { readonly extensionId = "rust-lang.rust-analyzer"; configureLang: vscode.Disposable | undefined; @@ -348,13 +355,20 @@ export class Config { return this.get<string>("statusBar.clickAction"); } - get statusBarDocumentSelector() { - return this.get<vscode.DocumentSelector>("statusBar.documentSelector"); + get statusBarShowStatusBar() { + return this.get<ShowStatusBar>("statusBar.showStatusBar"); } get initializeStopped() { return this.get<boolean>("initializeStopped"); } + + get askBeforeUpdateTest() { + return this.get<boolean>("runnables.askBeforeUpdateTest"); + } + async setAskBeforeUpdateTest(value: boolean) { + await this.cfg.update("runnables.askBeforeUpdateTest", value, true); + } } export function prepareVSCodeConfig<T>(resp: T): T { diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 459754b1d1c..37a54abf71f 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -480,14 +480,19 @@ export class Ctx implements RustAnalyzerExtensionApi { } private updateStatusBarVisibility(editor: vscode.TextEditor | undefined) { - const documentSelector = this.config.statusBarDocumentSelector; - if (documentSelector != null) { + const showStatusBar = this.config.statusBarShowStatusBar; + if (showStatusBar == null || showStatusBar === "never") { + this.statusBar.hide(); + } else if (showStatusBar === "always") { + this.statusBar.show(); + } else { + const documentSelector = showStatusBar.documentSelector; if (editor != null && vscode.languages.match(documentSelector, editor.document) > 0) { this.statusBar.show(); - return; + } else { + this.statusBar.hide(); } } - this.statusBar.hide(); } pushExtCleanup(d: Disposable) { diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 3be63741c00..517fdfe18ed 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -0eca4dd3205a01dba4bd7b7c140ec370aff03440 +fb546ee09b226bc4dd4b712d35a372d923c4fa54 diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index e7534582f2b..d5fe3236789 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -163,6 +163,13 @@ fn generate_nodes(kinds: KindsSrc, grammar: &AstSrc) -> String { quote! { impl AstNode for #name { #[inline] + fn kind() -> SyntaxKind + where + Self: Sized + { + #kind + } + #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == #kind } @@ -397,6 +404,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { }); let punctuation = grammar.punct.iter().map(|(_token, name)| format_ident!("{}", name)).collect::<Vec<_>>(); + let punctuation_texts = grammar.punct.iter().map(|&(text, _name)| text); let fmt_kw_as_variant = |&name| match name { "Self" => format_ident!("SELF_TYPE_KW"), @@ -422,6 +430,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { quote! { #kw if #ed <= edition } }) .collect::<Vec<_>>(); + let edition_dependent_keywords = grammar.edition_dependent_keywords.iter().map(|&(it, _)| it); let edition_dependent_keywords_variants = grammar .edition_dependent_keywords .iter() @@ -495,6 +504,20 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { use self::SyntaxKind::*; impl SyntaxKind { + #[allow(unreachable_patterns)] + pub const fn text(self) -> &'static str { + match self { + TOMBSTONE | EOF | __LAST + #( | #literals )* + #( | #nodes )* + #( | #tokens )* => panic!("no text for these `SyntaxKind`s"), + #( #punctuation => #punctuation_texts ,)* + #( #strict_keywords_variants => #strict_keywords ,)* + #( #contextual_keywords_variants => #contextual_keywords ,)* + #( #edition_dependent_keywords_variants => #edition_dependent_keywords ,)* + } + } + /// Checks whether this syntax kind is a strict keyword for the given edition. /// Strict keywords are identifiers that are always considered keywords. pub fn is_strict_keyword(self, edition: Edition) -> bool { @@ -861,7 +884,7 @@ fn lower_separated_list( if !matches!( repeat.as_slice(), [comma, nt_] - if trailing_sep.map_or(true, |it| comma == &**it) && match (nt, nt_) { + if trailing_sep.is_none_or(|it| comma == &**it) && match (nt, nt_) { (Either::Left(node), Rule::Node(nt_)) => node == nt_, (Either::Right(token), Rule::Token(nt_)) => token == nt_, _ => false, diff --git a/src/tools/rust-analyzer/xtask/src/main.rs b/src/tools/rust-analyzer/xtask/src/main.rs index 5c312da1dd7..1e723b90a5e 100644 --- a/src/tools/rust-analyzer/xtask/src/main.rs +++ b/src/tools/rust-analyzer/xtask/src/main.rs @@ -9,7 +9,12 @@ //! `.cargo/config`. #![warn(rust_2018_idioms, unused_lifetimes)] -#![allow(clippy::print_stderr, clippy::print_stdout)] +#![allow( + clippy::print_stderr, + clippy::print_stdout, + clippy::disallowed_methods, + clippy::disallowed_types +)] mod flags; diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 1f98e820840..b5b360db252 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata", @@ -161,9 +161,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "shlex", ] @@ -1446,9 +1446,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.93" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", @@ -1488,12 +1488,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys", @@ -1881,9 +1882,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf -Subproject d5055e78042c739deeb3fe0bef83fde0e5cc259 +Subproject e22e08623a349b13a3296971a05394d44f76974 diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs index 4078484ff10..34984798ae6 100644 --- a/src/tools/rustfmt/src/bin/main.rs +++ b/src/tools/rustfmt/src/bin/main.rs @@ -161,6 +161,12 @@ fn make_opts() -> Options { "Set options from command line. These settings take priority over .rustfmt.toml", "[key1=val1,key2=val2...]", ); + opts.optopt( + "", + "style-edition", + "The edition of the Style Guide.", + "[2015|2018|2021|2024]", + ); if is_nightly { opts.optflag( @@ -186,12 +192,6 @@ fn make_opts() -> Options { "skip-children", "Don't reformat child modules (unstable).", ); - opts.optopt( - "", - "style-edition", - "The edition of the Style Guide (unstable).", - "[2015|2018|2021|2024]", - ); } opts.optflag("v", "verbose", "Print verbose output"); diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 7b4730eadc8..1d88726d945 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -31,18 +31,31 @@ use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; /// - `[small, ntp]` /// - unary tuple constructor `([small, ntp])` /// - `&[small]` -pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool { +pub(crate) fn is_short_pattern( + context: &RewriteContext<'_>, + pat: &ast::Pat, + pat_str: &str, +) -> bool { // We also require that the pattern is reasonably 'small' with its literal width. - pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(pat) + pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(context, pat) } -fn is_short_pattern_inner(pat: &ast::Pat) -> bool { - match pat.kind { - ast::PatKind::Rest - | ast::PatKind::Never - | ast::PatKind::Wild - | ast::PatKind::Err(_) - | ast::PatKind::Lit(_) => true, +fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool { + match &pat.kind { + ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Err(_) => { + true + } + ast::PatKind::Expr(expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + _ => unreachable!(), + }, + ast::ExprKind::ConstBlock(_) | ast::ExprKind::Path(..) => { + context.config.style_edition() <= StyleEdition::Edition2024 + } + _ => unreachable!(), + }, ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), ast::PatKind::Struct(..) | ast::PatKind::MacCall(..) @@ -57,8 +70,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { ast::PatKind::Box(ref p) | PatKind::Deref(ref p) | ast::PatKind::Ref(ref p, _) - | ast::PatKind::Paren(ref p) => is_short_pattern_inner(&*p), - PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(p)), + | ast::PatKind::Paren(ref p) => is_short_pattern_inner(context, &*p), + PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(context, p)), } } @@ -96,7 +109,7 @@ impl Rewrite for Pat { let use_mixed_layout = pats .iter() .zip(pat_strs.iter()) - .all(|(pat, pat_str)| is_short_pattern(pat, pat_str)); + .all(|(pat, pat_str)| is_short_pattern(context, pat, pat_str)); let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect(); let tactic = if use_mixed_layout { DefinitiveListTactic::Mixed @@ -293,7 +306,7 @@ impl Rewrite for Pat { let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } - PatKind::Lit(ref expr) => expr.rewrite_result(context, shape), + PatKind::Expr(ref expr) => expr.rewrite_result(context, shape), PatKind::Slice(ref slice_pat) if context.config.style_edition() <= StyleEdition::Edition2021 => { @@ -530,7 +543,7 @@ pub(crate) fn can_be_overflowed_pat( ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => { can_be_overflowed_pat(context, &TuplePatField::Pat(p), len) } - ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len), + ast::PatKind::Expr(ref expr) => can_be_overflowed_expr(context, expr, len), _ => false, }, TuplePatField::Dotdot(..) => false, diff --git a/src/tools/rustfmt/tests/source/pattern.rs b/src/tools/rustfmt/tests/source/pattern.rs index f06d03cadf2..ed6ad690fa9 100644 --- a/src/tools/rustfmt/tests/source/pattern.rs +++ b/src/tools/rustfmt/tests/source/pattern.rs @@ -88,3 +88,13 @@ fn issue3728() { | c; foo((1,)); } + +fn literals() { + match 42 { + const { 1 + 2 } | 4 + | 6 => {} + 10 | 11 | 12 + | 13 | 14 => {} + _ => {} + } +} \ No newline at end of file diff --git a/src/tools/rustfmt/tests/target/pattern.rs b/src/tools/rustfmt/tests/target/pattern.rs index 576018ac623..e867f65929d 100644 --- a/src/tools/rustfmt/tests/target/pattern.rs +++ b/src/tools/rustfmt/tests/target/pattern.rs @@ -96,3 +96,11 @@ fn issue3728() { let foo = |(c,)| c; foo((1,)); } + +fn literals() { + match 42 { + const { 1 + 2 } | 4 | 6 => {} + 10 | 11 | 12 | 13 | 14 => {} + _ => {} + } +} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 912cbb668b0..d4cbc37f6d2 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -195,6 +195,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ ("cranelift-module", "Apache-2.0 WITH LLVM-exception"), ("cranelift-native", "Apache-2.0 WITH LLVM-exception"), ("cranelift-object", "Apache-2.0 WITH LLVM-exception"), + ("foldhash", "Zlib"), ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"), ("regalloc2", "Apache-2.0 WITH LLVM-exception"), ("target-lexicon", "Apache-2.0 WITH LLVM-exception"), @@ -502,7 +503,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start - "ahash", + "allocator-api2", "anyhow", "arbitrary", "bitflags", @@ -524,6 +525,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "crc32fast", "equivalent", "fallible-iterator", + "foldhash", "gimli", "hashbrown", "indexmap", @@ -533,7 +535,6 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "mach2", "memchr", "object", - "once_cell", "proc-macro2", "quote", "regalloc2", @@ -541,13 +542,11 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "rustc-hash", "serde", "serde_derive", - "slice-group-by", "smallvec", "stable_deref_trait", "syn", "target-lexicon", "unicode-ident", - "version_check", "wasmtime-jit-icache-coherence", "windows-sys", "windows-targets", @@ -559,8 +558,6 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", - "zerocopy", - "zerocopy-derive", // tidy-alphabetical-end ]; @@ -622,12 +619,17 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut b } // Remove the proc-macro crates themselves proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg])); - let proc_macro_deps_iter = proc_macro_deps.into_iter().map(|dep| metadata[dep].name.clone()); - if bless { - let mut proc_macro_deps: Vec<_> = proc_macro_deps_iter.collect(); + let proc_macro_deps: HashSet<_> = + proc_macro_deps.into_iter().map(|dep| metadata[dep].name.clone()).collect(); + let expected = proc_macro_deps::CRATES.iter().map(|s| s.to_string()).collect::<HashSet<_>>(); + + let needs_blessing = proc_macro_deps.difference(&expected).next().is_some() + || expected.difference(&proc_macro_deps).next().is_some(); + + if needs_blessing && bless { + let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect(); proc_macro_deps.sort(); - proc_macro_deps.dedup(); let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs")) .expect("`proc_macro_deps` should exist"); writeln!( @@ -649,10 +651,8 @@ pub static CRATES: &[&str] = &[ ) .unwrap(); } else { - let proc_macro_deps: HashSet<_> = proc_macro_deps_iter.collect(); - let expected = - proc_macro_deps::CRATES.iter().map(|s| s.to_string()).collect::<HashSet<_>>(); let old_bad = *bad; + for missing in proc_macro_deps.difference(&expected) { tidy_error!( bad, diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index aefcd2bb0cc..21b513629ed 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -72,6 +72,21 @@ const ANNOTATIONS_TO_IGNORE: &[&str] = &[ "//@ normalize-stderr", ]; +// If you edit this, also edit where it gets used in `check` (calling `contains_ignore_directives`) +const CONFIGURABLE_CHECKS: [&str; 11] = [ + "cr", + "undocumented-unsafe", + "tab", + "linelength", + "filelength", + "end-whitespace", + "trailing-newlines", + "leading-newlines", + "copyright", + "dbg", + "odd-backticks", +]; + fn generate_problems<'a>( consts: &'a [u32], letter_digit: &'a FxHashMap<char, char>, @@ -220,6 +235,7 @@ fn long_line_is_ok(extension: &str, is_error_code: bool, max_columns: usize, lin } } +#[derive(Clone, Copy)] enum Directive { /// By default, tidy always warns against style issues. Deny, @@ -231,20 +247,28 @@ enum Directive { Ignore(bool), } -fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) -> Directive { +// Use a fixed size array in the return type to catch mistakes with changing `CONFIGURABLE_CHECKS` +// without changing the code in `check` easier. +fn contains_ignore_directives<const N: usize>( + can_contain: bool, + contents: &str, + checks: [&str; N], +) -> [Directive; N] { if !can_contain { - return Directive::Deny; - } - // Update `can_contain` when changing this - if contents.contains(&format!("// ignore-tidy-{check}")) - || contents.contains(&format!("# ignore-tidy-{check}")) - || contents.contains(&format!("/* ignore-tidy-{check} */")) - || contents.contains(&format!("<!-- ignore-tidy-{check} -->")) - { - Directive::Ignore(false) - } else { - Directive::Deny + return [Directive::Deny; N]; } + checks.map(|check| { + // Update `can_contain` when changing this + if contents.contains(&format!("// ignore-tidy-{check}")) + || contents.contains(&format!("# ignore-tidy-{check}")) + || contents.contains(&format!("/* ignore-tidy-{check} */")) + || contents.contains(&format!("<!-- ignore-tidy-{check} -->")) + { + Directive::Ignore(false) + } else { + Directive::Deny + } + }) } macro_rules! suppressible_tidy_err { @@ -370,6 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) { COLS }; + // When you change this, also change the `directive_line_starts` variable below let can_contain = contents.contains("// ignore-tidy-") || contents.contains("# ignore-tidy-") || contents.contains("/* ignore-tidy-") @@ -385,22 +410,19 @@ pub fn check(path: &Path, bad: &mut bool) { return; } } - let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); - let mut skip_undocumented_unsafe = - contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); - let mut skip_tab = contains_ignore_directive(can_contain, &contents, "tab"); - let mut skip_line_length = contains_ignore_directive(can_contain, &contents, "linelength"); - let mut skip_file_length = contains_ignore_directive(can_contain, &contents, "filelength"); - let mut skip_end_whitespace = - contains_ignore_directive(can_contain, &contents, "end-whitespace"); - let mut skip_trailing_newlines = - contains_ignore_directive(can_contain, &contents, "trailing-newlines"); - let mut skip_leading_newlines = - contains_ignore_directive(can_contain, &contents, "leading-newlines"); - let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright"); - let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg"); - let mut skip_odd_backticks = - contains_ignore_directive(can_contain, &contents, "odd-backticks"); + let [ + mut skip_cr, + mut skip_undocumented_unsafe, + mut skip_tab, + mut skip_line_length, + mut skip_file_length, + mut skip_end_whitespace, + mut skip_trailing_newlines, + mut skip_leading_newlines, + mut skip_copyright, + mut skip_dbg, + mut skip_odd_backticks, + ] = contains_ignore_directives(can_contain, &contents, CONFIGURABLE_CHECKS); let mut leading_new_lines = false; let mut trailing_new_lines = 0; let mut lines = 0; @@ -474,6 +496,22 @@ pub fn check(path: &Path, bad: &mut bool) { suppressible_tidy_err!(err, skip_cr, "CR character"); } if !is_this_file { + let directive_line_starts = ["// ", "# ", "/* ", "<!-- "]; + let possible_line_start = + directive_line_starts.into_iter().any(|s| line.starts_with(s)); + let contains_potential_directive = + possible_line_start && (line.contains("-tidy") || line.contains("tidy-")); + let has_recognized_ignore_directive = + contains_ignore_directives(can_contain, line, CONFIGURABLE_CHECKS) + .into_iter() + .any(|directive| matches!(directive, Directive::Ignore(_))); + let has_alphabetical_directive = line.contains("tidy-alphabetical-start") + || line.contains("tidy-alphabetical-end"); + let has_recognized_directive = + has_recognized_ignore_directive || has_alphabetical_directive; + if contains_potential_directive && (!has_recognized_directive) { + err("Unrecognized tidy directive") + } // Allow using TODO in diagnostic suggestions by marking the // relevant line with `// ignore-tidy-todo`. if trimmed.contains("TODO") && !trimmed.contains("ignore-tidy-todo") { |
