Skip to content

Commit

Permalink
Define new QueuedTask variant Flycheck
Browse files Browse the repository at this point in the history
  • Loading branch information
alibektas committed Aug 16, 2024
1 parent dab022f commit e71a14c
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 6 deletions.
7 changes: 6 additions & 1 deletion crates/rust-analyzer/src/handlers/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,12 @@ pub(crate) fn handle_did_save_text_document(
}
}

if !state.config.check_on_save() || run_flycheck(state, vfs_path) {
if state.config.check_on_save() {
let _ = state
.deferred_task_queue
.sender
.send(crate::main_loop::QueuedTask::Flycheck(vfs_path));

return Ok(());
}
} else if state.config.check_on_save() {
Expand Down
122 changes: 121 additions & 1 deletion crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
use std::{
fmt,
ops::Div as _,
ops::{Deref, Div as _},
time::{Duration, Instant},
};

use always_assert::always;
use crossbeam_channel::{select, Receiver};
use ide_db::base_db::{SourceDatabase, SourceRootDatabase, VfsPath};
use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
use stdx::thread::ThreadIntent;
Expand All @@ -30,6 +31,7 @@ use crate::{
},
lsp_ext,
reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
target_spec::TargetSpec,
test_runner::{CargoTestMessage, TestState},
};

Expand Down Expand Up @@ -86,6 +88,7 @@ impl fmt::Display for Event {
pub(crate) enum QueuedTask {
CheckIfIndexed(lsp_types::Url),
CheckProcMacroSources(Vec<FileId>),
Flycheck(VfsPath),
}

#[derive(Debug)]
Expand Down Expand Up @@ -846,6 +849,123 @@ impl GlobalState {
}
});
}
QueuedTask::Flycheck(vfs_path) => {
let _p = tracing::info_span!("run_flycheck").entered();
let file_id = self.vfs.read().0.file_id(&vfs_path);
if let Some(file_id) = file_id {
let world = self.snapshot();
let mut updated = false;
let task = move || -> std::result::Result<(), ide::Cancelled> {
// Is the target binary? If so we let flycheck trigger only for the workspace that contains it.
let target_is_bin = TargetSpec::for_file(&world, file_id)?
.is_some_and(|x| x.target_kind() == project_model::TargetKind::Bin);

let crate_ids = if target_is_bin {
// Trigger flychecks for the only workspace which the binary crate belongs to
world
.analysis
.crates_for(file_id)?
.into_iter()
.unique()
.collect::<Vec<_>>()
} else {
// 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<_>>()
};

let crate_root_paths: Vec<_> = crate_ids
.iter()
.filter_map(|&crate_id| {
world
.analysis
.crate_root(crate_id)
.map(|file_id| {
world
.file_id_to_file_path(file_id)
.as_path()
.map(ToOwned::to_owned)
})
.transpose()
})
.collect::<ide::Cancellable<_>>()?;
let crate_root_paths: Vec<_> =
crate_root_paths.iter().map(Deref::deref).collect();

// 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 {
project_model::ProjectWorkspaceKind::Cargo {
cargo, ..
}
| project_model::ProjectWorkspaceKind::DetachedFile {
cargo: Some((cargo, _)),
..
} => cargo.packages().find_map(|pkg| {
let has_target_with_root =
cargo[pkg].targets.iter().any(|&it| {
crate_root_paths.contains(&cargo[it].root.as_path())
});
has_target_with_root.then(|| cargo[pkg].name.clone())
}),
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))
});

let saved_file = vfs_path.as_path().map(|p| p.to_owned());

// Find and trigger corresponding flychecks
for flycheck in world.flycheck.iter() {
for (id, package) in workspace_ids.clone() {
if id == flycheck.id() {
updated = true;
match package.filter(|_| !world.config.flycheck_workspace()) {
Some(package) => flycheck.restart_for_package(package),
None => flycheck.restart_workspace(saved_file.clone()),
}
continue;
}
}
}
// No specific flycheck was triggered, so let's trigger all of them.
if !updated {
for flycheck in world.flycheck.iter() {
flycheck.restart_workspace(saved_file.clone());
}
}
Ok(())
};
self.task_pool.handle.spawn_with_sender(
stdx::thread::ThreadIntent::Worker,
move |_| {
if let Err(e) = std::panic::catch_unwind(task) {
tracing::error!("flycheck task panicked: {e:?}")
}
},
);
} else {
error!("FileId for VfsPath could not be found. This caused a flycheck request to be ignored. Related path {}" , vfs_path);
}
}
}
}

Expand Down
122 changes: 118 additions & 4 deletions crates/rust-analyzer/tests/slow-tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ mod testdir;
use std::{collections::HashMap, path::PathBuf, time::Instant};

use lsp_types::{
notification::DidOpenTextDocument,
notification::{DidOpenTextDocument, DidSaveTextDocument},
request::{
CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
InlayHintRequest, InlayHintResolveRequest, WillRenameFiles, WorkspaceSymbolRequest,
},
CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range,
RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams,
DidSaveTextDocumentParams, DocumentFormattingParams, FileRename, FormattingOptions,
GotoDefinitionParams, HoverParams, InlayHint, InlayHintLabel, InlayHintParams,
PartialResultParams, Position, Range, RenameFilesParams, TextDocumentIdentifier,
TextDocumentItem, TextDocumentPositionParams, Url, WorkDoneProgressParams,
};
use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
use serde_json::json;
Expand Down Expand Up @@ -1394,3 +1395,116 @@ version = "0.0.0"

server.request::<WorkspaceSymbolRequest>(Default::default(), json!([]));
}

#[test]
/// When flycheck is run on a binary, only the binary crate
/// should be taken in consideration.
fn flycheck_only_for_single_crate() {
if skip_slow_tests() {
return;
}

let tmp_dir = TestDir::new();
let path_str = tmp_dir.path().to_owned();

let server = Project::with_fixture(
r#"
//- /foo/Cargo.toml
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.dependencies]
luib = { version = "0.1.0" , path = "./crates/luib" }
luib2 = { version = "0.1.0" , path = "./crates/luib2" }
//- /foo/crates/luib/Cargo.toml
[package]
name = "luib"
version = "0.1.0"
edition = "2021"
[dependencies]
luib2.workspace = true
//- /foo/crates/luib/src/lib.rs
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
//- /foo/crates/buin/Cargo.toml
[package]
name = "buin"
version = "0.1.0"
edition = "2021"
[dependencies]
luib.workspace = true
luib2.workspace = true
//- /foo/crates/buin/src/main.rs
use luib;
fn main() {
luib::add(3, 4);
println!("Hello, world!");
}
//- /foo/crates/luib2/Cargo.toml
[package]
name = "luib2"
version = "0.1.0"
edition = "2021"
[dependencies]
//- /foo/crates/luib2/src/lib.rs
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
"#,
)
.root("foo")
.server()
.wait_until_workspace_is_loaded();

let mut path_to_file = path_str;
path_to_file.push("foo");
path_to_file.push("crates");
path_to_file.push("buin");
path_to_file.push("src");
path_to_file.push("main.rs");

dbg!(&path_to_file);

server.notification::<DidSaveTextDocument>(DidSaveTextDocumentParams {
text_document: TextDocumentIdentifier {
uri: Url::parse(&format!("file://{}", path_to_file)).unwrap(),
},
text: None,
});
}

0 comments on commit e71a14c

Please sign in to comment.