fix: Make flycheck clearing dependency-aware

This commit is contained in:
Shoyu Vanilla 2025-09-18 03:21:01 +09:00
parent b12a129347
commit de3ad58b73
8 changed files with 213 additions and 43 deletions

View file

@ -9,7 +9,7 @@
use std::{cell::RefCell, io, mem, process::Command};
use base_db::Env;
use cargo_metadata::{Message, camino::Utf8Path};
use cargo_metadata::{Message, PackageId, camino::Utf8Path};
use cfg::CfgAtom;
use itertools::Itertools;
use la_arena::ArenaMap;
@ -18,6 +18,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize as _;
use stdx::never;
use toolchain::Tool;
use triomphe::Arc;
use crate::{
CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot,
@ -284,7 +285,7 @@ impl WorkspaceBuildScripts {
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
let mut by_id: FxHashMap<Arc<PackageId>, Package> = FxHashMap::default();
for package in workspace.packages() {
outputs.insert(package, BuildScriptOutput::default());
by_id.insert(workspace[package].id.clone(), package);
@ -323,7 +324,7 @@ impl WorkspaceBuildScripts {
// ideally this would be something like:
// with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)),
// but owned trait objects aren't a thing
mut with_output_for: impl FnMut(&str, &mut dyn FnMut(&str, &mut BuildScriptOutput)),
mut with_output_for: impl FnMut(&PackageId, &mut dyn FnMut(&str, &mut BuildScriptOutput)),
progress: &dyn Fn(String),
) -> io::Result<Option<String>> {
let errors = RefCell::new(String::new());
@ -346,7 +347,7 @@ impl WorkspaceBuildScripts {
match message {
Message::BuildScriptExecuted(mut message) => {
with_output_for(&message.package_id.repr, &mut |name, data| {
with_output_for(&message.package_id, &mut |name, data| {
progress(format!("build script {name} run"));
let cfgs = {
let mut acc = Vec::new();
@ -377,7 +378,7 @@ impl WorkspaceBuildScripts {
});
}
Message::CompilerArtifact(message) => {
with_output_for(&message.package_id.repr, &mut |name, data| {
with_output_for(&message.package_id, &mut |name, data| {
progress(format!("proc-macro {name} built"));
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;

View file

@ -5,7 +5,7 @@ use std::str::from_utf8;
use anyhow::Context;
use base_db::Env;
use cargo_metadata::{CargoOpt, MetadataCommand};
use cargo_metadata::{CargoOpt, MetadataCommand, PackageId};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
@ -14,6 +14,7 @@ use serde_json::from_value;
use span::Edition;
use stdx::process::spawn_with_streaming_output;
use toolchain::Tool;
use triomphe::Arc;
use crate::cargo_config_file::make_lockfile_copy;
use crate::{CfgOverrides, InvocationStrategy};
@ -155,8 +156,8 @@ pub struct PackageData {
pub features: FxHashMap<String, Vec<String>>,
/// List of features enabled on this package
pub active_features: Vec<String>,
/// String representation of package id
pub id: String,
/// Package id
pub id: Arc<PackageId>,
/// Authors as given in the `Cargo.toml`
pub authors: Vec<String>,
/// Description as given in the `Cargo.toml`
@ -173,6 +174,10 @@ pub struct PackageData {
pub rust_version: Option<semver::Version>,
/// The contents of [package.metadata.rust-analyzer]
pub metadata: RustAnalyzerPackageMetaData,
/// If this package is a member of the workspace, store all direct and transitive
/// dependencies as long as they are workspace members, to track dependency relationships
/// between members.
pub all_member_deps: Option<FxHashSet<Package>>,
}
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
@ -334,6 +339,8 @@ impl CargoWorkspace {
let mut is_virtual_workspace = true;
let mut requires_rustc_private = false;
let mut members = FxHashSet::default();
meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
for meta_pkg in meta.packages {
let cargo_metadata::Package {
@ -356,6 +363,7 @@ impl CargoWorkspace {
rust_version,
..
} = meta_pkg;
let id = Arc::new(id);
let meta = from_value::<PackageMetadata>(metadata).unwrap_or_default();
let edition = match edition {
cargo_metadata::Edition::E2015 => Edition::Edition2015,
@ -375,7 +383,7 @@ impl CargoWorkspace {
let manifest = ManifestPath::try_from(AbsPathBuf::assert(manifest_path)).unwrap();
is_virtual_workspace &= manifest != ws_manifest_path;
let pkg = packages.alloc(PackageData {
id: id.repr.clone(),
id: id.clone(),
name: name.to_string(),
version,
manifest: manifest.clone(),
@ -395,7 +403,11 @@ impl CargoWorkspace {
features: features.into_iter().collect(),
active_features: Vec::new(),
metadata: meta.rust_analyzer.unwrap_or_default(),
all_member_deps: None,
});
if is_member {
members.insert(pkg);
}
let pkg_data = &mut packages[pkg];
requires_rustc_private |= pkg_data.metadata.rustc_private;
pkg_by_id.insert(id, pkg);
@ -440,6 +452,43 @@ impl CargoWorkspace {
.extend(node.features.into_iter().map(|it| it.to_string()));
}
fn saturate_all_member_deps(
packages: &mut Arena<PackageData>,
to_visit: Package,
visited: &mut FxHashSet<Package>,
members: &FxHashSet<Package>,
) {
let pkg_data = &mut packages[to_visit];
if !visited.insert(to_visit) {
return;
}
let deps: Vec<_> = pkg_data
.dependencies
.iter()
.filter_map(|dep| {
let pkg = dep.pkg;
if members.contains(&pkg) { Some(pkg) } else { None }
})
.collect();
let mut all_member_deps = FxHashSet::from_iter(deps.iter().copied());
for dep in deps {
saturate_all_member_deps(packages, dep, visited, members);
if let Some(transitives) = &packages[dep].all_member_deps {
all_member_deps.extend(transitives);
}
}
packages[to_visit].all_member_deps = Some(all_member_deps);
}
let mut visited = FxHashSet::default();
for member in members.iter() {
saturate_all_member_deps(&mut packages, *member, &mut visited, &members);
}
CargoWorkspace {
packages,
targets,