Skip to content

Commit 8d89929

Browse files
authored
Merge pull request #18043 from cormacrelf/feature/rust-project-discovery
Configure flycheck using workspace.discoverConfig
2 parents 569d06d + 96f0185 commit 8d89929

File tree

15 files changed

+773
-170
lines changed

15 files changed

+773
-170
lines changed

crates/hir-ty/src/next_solver/infer/traits.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ impl ObligationCause {
5555
}
5656
}
5757

58+
impl Default for ObligationCause {
59+
#[inline]
60+
fn default() -> Self {
61+
Self::new()
62+
}
63+
}
64+
5865
/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for
5966
/// which the "impl_source" must be found. The process of finding an "impl_source" is
6067
/// called "resolving" the `Obligation`. This process consists of

crates/project-model/src/project_json.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ pub struct ProjectJson {
7878
runnables: Vec<Runnable>,
7979
}
8080

81+
impl std::ops::Index<CrateArrayIdx> for ProjectJson {
82+
type Output = Crate;
83+
fn index(&self, index: CrateArrayIdx) -> &Self::Output {
84+
&self.crates[index.0]
85+
}
86+
}
87+
8188
impl ProjectJson {
8289
/// Create a new ProjectJson instance.
8390
///
@@ -195,12 +202,11 @@ impl ProjectJson {
195202
&self.project_root
196203
}
197204

198-
pub fn crate_by_root(&self, root: &AbsPath) -> Option<Crate> {
205+
pub fn crate_by_root(&self, root: &AbsPath) -> Option<&Crate> {
199206
self.crates
200207
.iter()
201208
.filter(|krate| krate.is_workspace_member)
202209
.find(|krate| krate.root_module == root)
203-
.cloned()
204210
}
205211

206212
/// Returns the path to the project's manifest, if it exists.
@@ -214,8 +220,17 @@ impl ProjectJson {
214220
self.crates
215221
.iter()
216222
.filter(|krate| krate.is_workspace_member)
217-
.filter_map(|krate| krate.build.clone())
223+
.filter_map(|krate| krate.build.as_ref())
218224
.find(|build| build.build_file.as_std_path() == path)
225+
.cloned()
226+
}
227+
228+
pub fn crate_by_label(&self, label: &str) -> Option<&Crate> {
229+
// this is fast enough for now, but it's unfortunate that this is O(crates).
230+
self.crates
231+
.iter()
232+
.filter(|krate| krate.is_workspace_member)
233+
.find(|krate| krate.build.as_ref().is_some_and(|build| build.label == label))
219234
}
220235

221236
/// Returns the path to the project's manifest or root folder, if no manifest exists.
@@ -231,6 +246,10 @@ impl ProjectJson {
231246
pub fn runnables(&self) -> &[Runnable] {
232247
&self.runnables
233248
}
249+
250+
pub fn runnable_template(&self, kind: RunnableKind) -> Option<&Runnable> {
251+
self.runnables().iter().find(|r| r.kind == kind)
252+
}
234253
}
235254

236255
/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
@@ -258,6 +277,12 @@ pub struct Crate {
258277
pub build: Option<Build>,
259278
}
260279

280+
impl Crate {
281+
pub fn iter_deps(&self) -> impl ExactSizeIterator<Item = CrateArrayIdx> {
282+
self.deps.iter().map(|dep| dep.krate)
283+
}
284+
}
285+
261286
/// Additional, build-specific data about a crate.
262287
#[derive(Clone, Debug, Eq, PartialEq)]
263288
pub struct Build {
@@ -328,13 +353,21 @@ pub struct Runnable {
328353
/// The kind of runnable.
329354
#[derive(Debug, Clone, PartialEq, Eq)]
330355
pub enum RunnableKind {
356+
/// `cargo check`, basically, with human-readable output.
331357
Check,
332358

333359
/// Can run a binary.
360+
/// May include {label} which will get the label from the `build` section of a crate.
334361
Run,
335362

336363
/// Run a single test.
364+
/// May include {label} which will get the label from the `build` section of a crate.
365+
/// May include {test_id} which will get the test clicked on by the user.
337366
TestOne,
367+
368+
/// Template for checking a target, emitting rustc JSON diagnostics.
369+
/// May include {label} which will get the label from the `build` section of a crate.
370+
Flycheck,
338371
}
339372

340373
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
@@ -441,6 +474,7 @@ pub struct RunnableData {
441474
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
442475
#[serde(rename_all = "camelCase")]
443476
pub enum RunnableKindData {
477+
Flycheck,
444478
Check,
445479
Run,
446480
TestOne,
@@ -511,6 +545,7 @@ impl From<RunnableKindData> for RunnableKind {
511545
RunnableKindData::Check => RunnableKind::Check,
512546
RunnableKindData::Run => RunnableKind::Run,
513547
RunnableKindData::TestOne => RunnableKind::TestOne,
548+
RunnableKindData::Flycheck => RunnableKind::Flycheck,
514549
}
515550
}
516551
}

crates/rust-analyzer/src/config.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,8 @@ config_data! {
480480

481481
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
482482
///
483-
/// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
484-
/// `progress_label` is used for the title in progress indicators, whereas `files_to_watch`
483+
/// [`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
484+
/// `progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
485485
/// is used to determine which build system-specific files should be watched in order to
486486
/// reload rust-analyzer.
487487
///
@@ -490,16 +490,17 @@ config_data! {
490490
/// "rust-analyzer.workspace.discoverConfig": {
491491
/// "command": [
492492
/// "rust-project",
493-
/// "develop-json"
493+
/// "develop-json",
494+
/// "{arg}"
494495
/// ],
495-
/// "progressLabel": "rust-analyzer",
496+
/// "progressLabel": "buck2/rust-project",
496497
/// "filesToWatch": [
497498
/// "BUCK"
498499
/// ]
499500
/// }
500501
/// ```
501502
///
502-
/// ## On `DiscoverWorkspaceConfig::command`
503+
/// ## Workspace Discovery Protocol
503504
///
504505
/// **Warning**: This format is provisional and subject to change.
505506
///
@@ -870,10 +871,18 @@ config_data! {
870871
/// (i.e., the folder containing the `Cargo.toml`). This can be overwritten
871872
/// by changing `#rust-analyzer.check.invocationStrategy#`.
872873
///
873-
/// If `$saved_file` is part of the command, rust-analyzer will pass
874-
/// the absolute path of the saved file to the provided command. This is
875-
/// intended to be used with non-Cargo build systems.
876-
/// Note that `$saved_file` is experimental and may be removed in the future.
874+
/// It supports two interpolation syntaxes, both mainly intended to be used with
875+
/// [non-Cargo build systems](./non_cargo_based_projects.md):
876+
///
877+
/// - If `{saved_file}` is part of the command, rust-analyzer will pass
878+
/// the absolute path of the saved file to the provided command.
879+
/// (A previous version, `$saved_file`, also works.)
880+
/// - If `{label}` is part of the command, rust-analyzer will pass the
881+
/// Cargo package ID, which can be used with `cargo check -p`, or a build label from
882+
/// `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like
883+
/// [`"rust-analyzer.check.workspace": false`](#check.workspace).
884+
///
885+
///
877886
///
878887
/// An example command would be:
879888
///
@@ -2431,6 +2440,8 @@ impl Config {
24312440

24322441
pub(crate) fn cargo_test_options(&self, source_root: Option<SourceRootId>) -> CargoOptions {
24332442
CargoOptions {
2443+
// Might be nice to allow users to specify test_command = "nextest"
2444+
subcommand: "test".into(),
24342445
target_tuples: self.cargo_target(source_root).clone().into_iter().collect(),
24352446
all_targets: false,
24362447
no_default_features: *self.cargo_noDefaultFeatures(source_root),
@@ -2464,9 +2475,9 @@ impl Config {
24642475
},
24652476
}
24662477
}
2467-
Some(_) | None => FlycheckConfig::CargoCommand {
2468-
command: self.check_command(source_root).clone(),
2469-
options: CargoOptions {
2478+
Some(_) | None => FlycheckConfig::Automatic {
2479+
cargo_options: CargoOptions {
2480+
subcommand: self.check_command(source_root).clone(),
24702481
target_tuples: self
24712482
.check_targets(source_root)
24722483
.clone()
@@ -4171,8 +4182,8 @@ mod tests {
41714182
assert_eq!(config.cargo_targetDir(None), &None);
41724183
assert!(matches!(
41734184
config.flycheck(None),
4174-
FlycheckConfig::CargoCommand {
4175-
options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. },
4185+
FlycheckConfig::Automatic {
4186+
cargo_options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. },
41764187
..
41774188
}
41784189
));
@@ -4195,8 +4206,8 @@ mod tests {
41954206
Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()));
41964207
assert!(matches!(
41974208
config.flycheck(None),
4198-
FlycheckConfig::CargoCommand {
4199-
options: CargoOptions { target_dir_config, .. },
4209+
FlycheckConfig::Automatic {
4210+
cargo_options: CargoOptions { target_dir_config, .. },
42004211
..
42014212
} if target_dir_config.target_dir(Some(&ws_target_dir)).map(Cow::into_owned)
42024213
== Some(ws_target_dir.join("rust-analyzer"))
@@ -4221,8 +4232,8 @@ mod tests {
42214232
);
42224233
assert!(matches!(
42234234
config.flycheck(None),
4224-
FlycheckConfig::CargoCommand {
4225-
options: CargoOptions { target_dir_config, .. },
4235+
FlycheckConfig::Automatic {
4236+
cargo_options: CargoOptions { target_dir_config, .. },
42264237
..
42274238
} if target_dir_config.target_dir(None).map(Cow::into_owned)
42284239
== Some(Utf8PathBuf::from("other_folder"))

crates/rust-analyzer/src/diagnostics.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ pub(crate) mod flycheck_to_proto;
33

44
use std::mem;
55

6-
use cargo_metadata::PackageId;
76
use ide::FileId;
87
use ide_db::{FxHashMap, base_db::DbPanicContext};
98
use itertools::Itertools;
@@ -12,10 +11,13 @@ use smallvec::SmallVec;
1211
use stdx::iter_eq_by;
1312
use triomphe::Arc;
1413

15-
use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind};
14+
use crate::{
15+
flycheck::PackageSpecifier, global_state::GlobalStateSnapshot, lsp, lsp_ext,
16+
main_loop::DiagnosticsTaskKind,
17+
};
1618

1719
pub(crate) type CheckFixes =
18-
Arc<Vec<FxHashMap<Option<Arc<PackageId>>, FxHashMap<FileId, Vec<Fix>>>>>;
20+
Arc<Vec<FxHashMap<Option<PackageSpecifier>, FxHashMap<FileId, Vec<Fix>>>>>;
1921

2022
#[derive(Debug, Default, Clone)]
2123
pub struct DiagnosticsMapConfig {
@@ -29,7 +31,7 @@ pub(crate) type DiagnosticsGeneration = usize;
2931

3032
#[derive(Debug, Clone, Default)]
3133
pub(crate) struct WorkspaceFlycheckDiagnostic {
32-
pub(crate) per_package: FxHashMap<Option<Arc<PackageId>>, PackageFlycheckDiagnostic>,
34+
pub(crate) per_package: FxHashMap<Option<PackageSpecifier>, PackageFlycheckDiagnostic>,
3335
}
3436

3537
#[derive(Debug, Clone)]
@@ -85,7 +87,7 @@ impl DiagnosticCollection {
8587
pub(crate) fn clear_check_for_package(
8688
&mut self,
8789
flycheck_id: usize,
88-
package_id: Arc<PackageId>,
90+
package_id: PackageSpecifier,
8991
) {
9092
let Some(check) = self.check.get_mut(flycheck_id) else {
9193
return;
@@ -124,7 +126,7 @@ impl DiagnosticCollection {
124126
pub(crate) fn clear_check_older_than_for_package(
125127
&mut self,
126128
flycheck_id: usize,
127-
package_id: Arc<PackageId>,
129+
package_id: PackageSpecifier,
128130
generation: DiagnosticsGeneration,
129131
) {
130132
let Some(check) = self.check.get_mut(flycheck_id) else {
@@ -154,7 +156,7 @@ impl DiagnosticCollection {
154156
&mut self,
155157
flycheck_id: usize,
156158
generation: DiagnosticsGeneration,
157-
package_id: &Option<Arc<PackageId>>,
159+
package_id: &Option<PackageSpecifier>,
158160
file_id: FileId,
159161
diagnostic: lsp_types::Diagnostic,
160162
fix: Option<Box<Fix>>,

0 commit comments

Comments
 (0)