Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/hir-ty/src/layout/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn current_machine_target_data() -> TargetData {
QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()),
None,
&FxHashMap::default(),
None,
)
.unwrap()
}
Expand Down
4 changes: 1 addition & 3 deletions crates/project-model/src/build_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,7 @@ impl WorkspaceBuildScripts {
cmd.arg(target_dir.as_ref());
}

if let Some(target) = &config.target {
cmd.args(["--target", target]);
}
toolchain::cargo_use_targets(toolchain, &mut cmd, config.target.as_slice());
let mut lockfile_copy = None;
if let Some(toolchain) = toolchain {
let lockfile_path =
Expand Down
15 changes: 12 additions & 3 deletions crates/project-model/src/cargo_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,9 +704,18 @@ impl FetchMetadata {
}

if !config.targets.is_empty() {
other_options.extend(
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
);
let mut has_json_target = false;
other_options.extend(config.targets.iter().flat_map(|target| {
has_json_target |= target.ends_with(".json");
["--filter-platform".to_owned(), target.clone()]
}));
if has_json_target
&& config.toolchain_version.as_ref().is_some_and(|version| {
*version >= toolchain::MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG
})
{
other_options.push("-Zjson-target-spec".to_owned());
}
}

command.other_options(other_options.clone());
Expand Down
13 changes: 8 additions & 5 deletions crates/project-model/src/toolchain_info/target_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub fn get(
config: QueryConfig<'_>,
target: Option<&str>,
extra_env: &FxHashMap<String, Option<String>>,
version: Option<&semver::Version>,
) -> anyhow::Result<target::TargetData> {
const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"];
let process = |output: String| {
Expand All @@ -55,9 +56,7 @@ pub fn get(
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
if let Some(target) = target {
cmd.args(["--target", target]);
}
toolchain::cargo_use_targets(version, &mut cmd, target.as_slice());
cmd.args(["--", "-Z", "unstable-options"]);
match utf8_stdout(&mut cmd) {
Ok(output) => return process(output),
Expand Down Expand Up @@ -95,13 +94,17 @@ mod tests {
let manifest_path =
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
let extra_env = &FxHashMap::default();
let Ok(version) = super::super::version::get(cfg, extra_env) else { return };
assert!(get(cfg, None, extra_env, version.as_ref()).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());
let extra_env = &FxHashMap::default();
let Ok(version) = super::super::version::get(cfg, extra_env) else { return };
assert!(get(cfg, None, extra_env, version.as_ref()).is_ok());
}
}
2 changes: 1 addition & 1 deletion crates/project-model/src/toolchain_info/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use toolchain::Tool;

use crate::{toolchain_info::QueryConfig, utf8_stdout};

pub(crate) fn get(
pub fn get(
config: QueryConfig<'_>,
extra_env: &FxHashMap<String, Option<String>>,
) -> Result<Option<Version>, anyhow::Error> {
Expand Down
29 changes: 20 additions & 9 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,13 +316,18 @@ impl ProjectWorkspace {
let target_data = Builder::new()
.name("ProjectWorkspace::target_data".to_owned())
.spawn_scoped(s, || {
target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
.inspect_err(|e| {
tracing::error!(%e,
"failed fetching data layout for \
{cargo_toml:?} workspace"
)
})
target_data::get(
toolchain_config,
targets.first().map(Deref::deref),
extra_env,
toolchain.as_ref(),
)
.inspect_err(|e| {
tracing::error!(%e,
"failed fetching data layout for \
{cargo_toml:?} workspace"
)
})
})
.expect("failed to spawn thread");

Expand Down Expand Up @@ -495,7 +500,12 @@ impl ProjectWorkspace {
rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
});
let data_layout = s.spawn(|| {
target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
target_data::get(
query_config,
targets.first().map(Deref::deref),
&config.extra_env,
toolchain.as_ref(),
)
});
let loaded_sysroot = s.spawn(|| {
if let Some(sysroot_project) = sysroot_project {
Expand Down Expand Up @@ -564,7 +574,8 @@ impl ProjectWorkspace {
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 target_data = target_data::get(query_config, None, &config.extra_env);
let target_data =
target_data::get(query_config, None, &config.extra_env, toolchain.as_ref());

let loaded_sysroot = sysroot.load_workspace(
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
Expand Down
7 changes: 5 additions & 2 deletions crates/rust-analyzer/src/cli/rustc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
use ide_db::base_db;
use itertools::Either;
use profile::StopWatch;
use project_model::toolchain_info::{QueryConfig, target_data};
use project_model::toolchain_info::{QueryConfig, target_data, version};
use project_model::{
CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource,
RustSourceWorkspaceConfig, Sysroot,
Expand Down Expand Up @@ -81,10 +81,13 @@ impl Tester {
sysroot.set_workspace(loaded_sysroot);
}

let query_config = QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref());
let toolchain_version = version::get(query_config, &cargo_config.extra_env).ok().flatten();
let target_data = target_data::get(
QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
query_config,
None,
&cargo_config.extra_env,
toolchain_version.as_ref(),
);

let workspace = ProjectWorkspace {
Expand Down
11 changes: 8 additions & 3 deletions crates/rust-analyzer/src/flycheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ impl CargoOptions {
cmd: &mut Command,
ws_target_dir: Option<&Utf8Path>,
package_repr: Option<&str>,
toolchain_version: Option<&semver::Version>,
) {
for target in &self.target_tuples {
cmd.args(["--target", target.as_str()]);
}
toolchain::cargo_use_targets(toolchain_version, cmd, &self.target_tuples);
if self.all_targets {
if self.set_test {
cmd.arg("--all-targets");
Expand Down Expand Up @@ -227,6 +226,7 @@ impl FlycheckHandle {
workspace_root: AbsPathBuf,
manifest_path: Option<AbsPathBuf>,
ws_target_dir: Option<Utf8PathBuf>,
toolchain_version: Option<semver::Version>,
) -> FlycheckHandle {
let actor = FlycheckActor::new(
id,
Expand All @@ -238,6 +238,7 @@ impl FlycheckHandle {
workspace_root,
manifest_path,
ws_target_dir,
toolchain_version,
);
let (sender, receiver) = unbounded::<StateChange>();
let thread =
Expand Down Expand Up @@ -445,6 +446,7 @@ struct FlycheckActor {
command_receiver: Option<Receiver<CheckMessage>>,
diagnostics_cleared_for: FxHashSet<PackageSpecifier>,
diagnostics_received: DiagnosticsReceived,
toolchain_version: Option<semver::Version>,
}

#[derive(PartialEq, Debug)]
Expand Down Expand Up @@ -531,6 +533,7 @@ impl FlycheckActor {
workspace_root: AbsPathBuf,
manifest_path: Option<AbsPathBuf>,
ws_target_dir: Option<Utf8PathBuf>,
toolchain_version: Option<semver::Version>,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
FlycheckActor {
Expand All @@ -548,6 +551,7 @@ impl FlycheckActor {
command_receiver: None,
diagnostics_cleared_for: Default::default(),
diagnostics_received: DiagnosticsReceived::NotYet,
toolchain_version,
}
}

Expand Down Expand Up @@ -958,6 +962,7 @@ impl FlycheckActor {
&mut cmd,
self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path),
package_repr,
self.toolchain_version.as_ref(),
);
cmd.args(&cargo_options.extra_args);
Some((cmd, FlycheckCommandOrigin::Cargo))
Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/handlers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ pub(crate) fn handle_run_test(
Some(cargo.target_directory().as_ref()),
target,
state.test_run_sender.clone(),
ws.toolchain.as_ref(),
)?;
handles.push(handle);
}
Expand Down
32 changes: 21 additions & 11 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ impl GlobalState {
self.config.default_root_path().clone(),
None,
None,
None,
)]
}
crate::flycheck::InvocationStrategy::PerWorkspace => {
Expand Down Expand Up @@ -942,21 +943,30 @@ impl GlobalState {
ProjectWorkspaceKind::DetachedFile { .. } => return None,
},
ws.sysroot.root().map(ToOwned::to_owned),
ws.toolchain.clone(),
))
})
.map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| {
FlycheckHandle::spawn(
.map(
|(
id,
generation.clone(),
sender.clone(),
config.clone(),
config_json,
(config_json, root, manifest_path, target_dir),
sysroot_root,
root.to_path_buf(),
manifest_path.map(|it| it.to_path_buf()),
target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()),
)
})
toolchain,
)| {
FlycheckHandle::spawn(
id,
generation.clone(),
sender.clone(),
config.clone(),
config_json,
sysroot_root,
root.to_path_buf(),
manifest_path.map(|it| it.to_path_buf()),
target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()),
toolchain,
)
},
)
.collect()
}
}
Expand Down
8 changes: 7 additions & 1 deletion crates/rust-analyzer/src/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ impl CargoTestHandle {
ws_target_dir: Option<&Utf8Path>,
test_target: TestTarget,
sender: Sender<CargoTestMessage>,
toolchain_version: Option<&semver::Version>,
) -> anyhow::Result<Self> {
let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env);
cmd.env("RUSTC_BOOTSTRAP", "1");
Expand All @@ -131,7 +132,12 @@ impl CargoTestHandle {
cmd.arg("--no-fail-fast");
cmd.arg("--manifest-path");
cmd.arg(root.join("Cargo.toml"));
options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package));
options.apply_on_command(
&mut cmd,
ws_target_dir,
Some(&test_target.package),
toolchain_version,
);
cmd.arg("--");
if let Some(path) = path {
cmd.arg(path);
Expand Down
1 change: 1 addition & 0 deletions crates/toolchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ doctest = false

[dependencies]
camino.workspace = true
semver.workspace = true

[lints]
workspace = true
30 changes: 30 additions & 0 deletions crates/toolchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,33 @@ pub fn probe_for_binary(path: Utf8PathBuf) -> Option<Utf8PathBuf> {
};
iter::once(path).chain(with_extension).find(|it| it.is_file())
}

pub const MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG: semver::Version =
semver::Version {
major: 1,
minor: 95,
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};

/// Uses targets in a Cargo process.
pub fn cargo_use_targets(
toolchain_version: Option<&semver::Version>,
cmd: &mut Command,
targets: impl IntoIterator<Item = impl AsRef<str>>,
) {
let mut has_json_target = false;
for target in targets {
let target = target.as_ref();
cmd.args(["--target", target]);
has_json_target |= target.ends_with(".json");
}
if has_json_target
&& toolchain_version.is_some_and(|version| {

@ShoyuVanilla ShoyuVanilla Aug 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit uncertain on whether we should make this default when the toolchain_version.is_none(), as it will fail when the toolchain is empty but actually 1.95.0 =<, like in flychecks with InvocationStrategy::Once.
But as you have written #23012 this would be okay

View changes since the review

*version >= MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG
})
{
cmd.arg("-Zjson-target-spec");
}
}