Repository: rust-lang/rust-analyzer
Pull request #17062 (2024-04-13 20:45:55)

Temporarily disable unlinked file popup
Not gonna look into this until the next release, so I'd rather disable it for the time being


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index a10345a706..08ad10c297 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -91,7 +91,7 @@ jobs:
         run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
 
       - name: Test
-        if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
+        if: matrix.os == 'ubuntu-latest' || github.event_name == 'push'
         run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
 
       - name: Switch to stable toolchain
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 11014338d7..3ad7a37ec1 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -95,7 +95,7 @@ jobs:
         run: sudo apt-get install gcc-arm-linux-gnueabihf
 
       - name: Dist
-        run: cargo xtask dist --client-patch-version ${{ github.run_number }}
+        run: cargo xtask dist --jemalloc --client-patch-version ${{ github.run_number }}
 
       - run: npm ci
         working-directory: editors/code
@@ -159,7 +159,7 @@ jobs:
           fetch-depth: ${{ env.FETCH_DEPTH }}
 
       - name: Dist
-        run: cargo xtask dist --client-patch-version ${{ github.run_number }}
+        run: cargo xtask dist --jemalloc --client-patch-version ${{ github.run_number }}
 
       - run: npm ci
         working-directory: editors/code
diff --git a/crates/flycheck/src/command.rs b/crates/flycheck/src/command.rs
index 8ba7018316..091146a001 100644
--- a/crates/flycheck/src/command.rs
+++ b/crates/flycheck/src/command.rs
@@ -4,13 +4,12 @@
 use std::{
     ffi::OsString,
     fmt, io,
-    marker::PhantomData,
     path::PathBuf,
     process::{ChildStderr, ChildStdout, Command, Stdio},
 };
 
 use command_group::{CommandGroup, GroupChild};
-use crossbeam_channel::Sender;
+use crossbeam_channel::{unbounded, Receiver, Sender};
 use stdx::process::streaming_output;
 
 /// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of
@@ -100,10 +99,10 @@ pub(crate) struct CommandHandle<T> {
     /// a read syscall dropping and therefore terminating the process is our best option.
     child: JodGroupChild,
     thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
+    pub(crate) receiver: Receiver<T>,
     program: OsString,
     arguments: Vec<OsString>,
     current_dir: Option<PathBuf>,
-    _phantom: PhantomData<T>,
 }
 
 impl<T> fmt::Debug for CommandHandle<T> {
@@ -117,7 +116,7 @@ impl<T> fmt::Debug for CommandHandle<T> {
 }
 
 impl<T: ParseFromLine> CommandHandle<T> {
-    pub(crate) fn spawn(mut command: Command, sender: Sender<T>) -> std::io::Result<Self> {
+    pub(crate) fn spawn(mut command: Command) -> std::io::Result<Self> {
         command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
         let mut child = command.group_spawn().map(JodGroupChild)?;
 
@@ -128,12 +127,13 @@ impl<T: ParseFromLine> CommandHandle<T> {
         let stdout = child.0.inner().stdout.take().unwrap();
         let stderr = child.0.inner().stderr.take().unwrap();
 
+        let (sender, receiver) = unbounded();
         let actor = CargoActor::<T>::new(sender, stdout, stderr);
         let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
             .name("CommandHandle".to_owned())
             .spawn(move || actor.run())
             .expect("failed to spawn thread");
-        Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData })
+        Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
     }
 
     pub(crate) fn cancel(mut self) {
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 5dfaaf7742..4ee86954ac 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -41,50 +41,19 @@ pub enum InvocationLocation {
     Workspace,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct CargoOptions {
-    pub target_triples: Vec<String>,
-    pub all_targets: bool,
-    pub no_default_features: bool,
-    pub all_features: bool,
-    pub features: Vec<String>,
-    pub extra_args: Vec<String>,
-    pub extra_env: FxHashMap<String, String>,
-    pub target_dir: Option<Utf8PathBuf>,
-}
-
-impl CargoOptions {
-    fn apply_on_command(&self, cmd: &mut Command) {
-        for target in &self.target_triples {
-            cmd.args(["--target", target.as_str()]);
-        }
-        if self.all_targets {
-            cmd.arg("--all-targets");
-        }
-        if self.all_features {
-            cmd.arg("--all-features");
-        } else {
-            if self.no_default_features {
-                cmd.arg("--no-default-features");
-            }
-            if !self.features.is_empty() {
-                cmd.arg("--features");
-                cmd.arg(self.features.join(" "));
-            }
-        }
-        if let Some(target_dir) = &self.target_dir {
-            cmd.arg("--target-dir").arg(target_dir);
-        }
-        cmd.envs(&self.extra_env);
-    }
-}
-
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum FlycheckConfig {
     CargoCommand {
         command: String,
-        options: CargoOptions,
+        target_triples: Vec<String>,
+        all_targets: bool,
+        no_default_features: bool,
+        all_features: bool,
+        features: Vec<String>,
+        extra_args: Vec<String>,
+        extra_env: FxHashMap<String, String>,
         ansi_color_output: bool,
+        target_dir: Option<Utf8PathBuf>,
     },
     CustomCommand {
         command: String,
@@ -215,8 +184,6 @@ struct FlycheckActor {
     /// have to wrap sub-processes output handling in a thread and pass messages
     /// back over a channel.
     command_handle: Option<CommandHandle<CargoCheckMessage>>,
-    /// The receiver side of the channel mentioned above.
-    command_receiver: Option<Receiver<CargoCheckMessage>>,
 }
 
 enum Event {
@@ -242,7 +209,6 @@ impl FlycheckActor {
             sysroot_root,
             root: workspace_root,
             command_handle: None,
-            command_receiver: None,
         }
     }
 
@@ -251,13 +217,14 @@ impl FlycheckActor {
     }
 
     fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
+        let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
         if let Ok(msg) = inbox.try_recv() {
             // give restarts a preference so check outputs don't block a restart or stop
             return Some(Event::RequestStateChange(msg));
         }
         select! {
             recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
-            recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
+            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
 
@@ -286,12 +253,10 @@ impl FlycheckActor {
                     let formatted_command = format!("{:?}", command);
 
                     tracing::debug!(?command, "will restart flycheck");
-                    let (sender, receiver) = unbounded();
-                    match CommandHandle::spawn(command, sender) {
+                    match CommandHandle::spawn(command) {
                         Ok(command_handle) => {
-                            tracing::debug!(command = formatted_command, "did restart flycheck");
+                            tracing::debug!(command = formatted_command, "did  restart flycheck");
                             self.command_handle = Some(command_handle);
-                            self.command_receiver = Some(receiver);
                             self.report_progress(Progress::DidStart);
                         }
                         Err(error) => {
@@ -307,15 +272,13 @@ impl FlycheckActor {
 
                     // Watcher finished
                     let command_handle = self.command_handle.take().unwrap();
-                    self.command_receiver.take();
                     let formatted_handle = format!("{:?}", command_handle);
 
                     let res = command_handle.join();
-                    if let Err(error) = &res {
+                    if res.is_err() {
                         tracing::error!(
-                            "Flycheck failed to run the following command: {}, error={}",
-                            formatted_handle,
-                            error
+                            "Flycheck failed to run the following command: {}",
+                            formatted_handle
                         );
                     }
                     self.report_progress(Progress::DidFinish(res));
@@ -369,7 +332,18 @@ impl FlycheckActor {
         saved_file: Option<&AbsPath>,
     ) -> Option<Command> {
         let (mut cmd, args) = match &self.config {
-            FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
+            FlycheckConfig::CargoCommand {
+                command,
+                target_triples,
+                no_default_features,
+                all_targets,
+                all_features,
+                extra_args,
+                features,
+                extra_env,
+                ansi_color_output,
+                target_dir,
+            } => {
                 let mut cmd = Command::new(Tool::Cargo.path());
                 if let Some(sysroot_root) = &self.sysroot_root {
                     cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
@@ -391,8 +365,28 @@ impl FlycheckActor {
                 cmd.arg("--manifest-path");
                 cmd.arg(self.root.join("Cargo.toml"));
 
-                options.apply_on_command(&mut cmd);
-                (cmd, options.extra_args.clone())
+                for target in target_triples {
+                    cmd.args(["--target", target.as_str()]);
+                }
+                if *all_targets {
+                    cmd.arg("--all-targets");
+                }
+                if *all_features {
+                    cmd.arg("--all-features");
+                } else {
+                    if *no_default_features {
+                        cmd.arg("--no-default-features");
+                    }
+                    if !features.is_empty() {
+                        cmd.arg("--features");
+                        cmd.arg(features.join(" "));
+                    }
+                }
+                if let Some(target_dir) = target_dir {
+                    cmd.arg("--target-dir").arg(target_dir);
+                }
+                cmd.envs(extra_env);
+                (cmd, extra_args.clone())
             }
             FlycheckConfig::CustomCommand {
                 command,
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index db94a21a6d..ff051fa870 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -175,7 +175,8 @@ fn add_func_to_accumulator(
         edit.edit_file(file);
 
         let target = function_builder.target.clone();
-        let func = function_builder.render(ctx.config.snippet_cap, edit);
+        let function_template = function_builder.render();
+        let func = function_template.to_ast(ctx.config.snippet_cap, edit);
 
         if let Some(name) = adt_name {
             let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db()))));
@@ -204,6 +205,37 @@ fn get_adt_source(
     find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id))
 }
 
+struct FunctionTemplate {
+    fn_def: ast::Fn,
+    ret_type: Option<ast::RetType>,
+    should_focus_return_type: bool,
+    tail_expr: ast::Expr,
+}
+
+impl FunctionTemplate {
+    fn to_ast(&self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
+        let Self { fn_def, ret_type, should_focus_return_type, tail_expr } = self;
+
+        if let Some(cap) = cap {
+            if *should_focus_return_type {
+                // Focus the return type if there is one
+                match ret_type {
+                    Some(ret_type) => {
+                        edit.add_placeholder_snippet(cap, ret_type.clone());
+                    }
+                    None => {
+                        edit.add_placeholder_snippet(cap, tail_expr.clone());
+                    }
+                }
+            } else {
+                edit.add_placeholder_snippet(cap, tail_expr.clone());
+            }
+        }
+
+        fn_def.clone()
+    }
+}
+
 struct FunctionBuilder {
     target: GeneratedFunctionTarget,
     fn_name: ast::Name,
@@ -307,7 +339,7 @@ impl FunctionBuilder {
         })
     }
 
-    fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
+    fn render(self) -> FunctionTemplate {
         let placeholder_expr = make::ext::expr_todo();
         let fn_body = make::block_expr(vec![], Some(placeholder_expr));
         let visibility = match self.visibility {
@@ -329,31 +361,17 @@ impl FunctionBuilder {
         )
         .clone_for_update();
 
-        let ret_type = fn_def.ret_type();
-        // PANIC: we guarantee we always create a function body with a tail expr
-        let tail_expr = fn_def
-            .body()
-            .expect("generated function should have a body")
-            .tail_expr()
-            .expect("function body should have a tail expression");
-
-        if let Some(cap) = cap {
-            if self.should_focus_return_type {
-                // Focus the return type if there is one
-                match ret_type {
-                    Some(ret_type) => {
-                        edit.add_placeholder_snippet(cap, ret_type.clone());
-                    }
-                    None => {
-                        edit.add_placeholder_snippet(cap, tail_expr.clone());
-                    }
-                }
-            } else {
-                edit.add_placeholder_snippet(cap, tail_expr.clone());
-            }
+        FunctionTemplate {
+            ret_type: fn_def.ret_type(),
+            // PANIC: we guarantee we always create a function body with a tail expr
+            tail_expr: fn_def
+                .body()
+                .expect("generated function should have a body")
+                .tail_expr()
+                .expect("function body should have a tail expression"),
+            should_focus_return_type: self.should_focus_return_type,
+            fn_def,
         }
-
-        fn_def
     }
 }
 
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index c6e243b31a..ae061c8ce3 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -336,8 +336,7 @@ fn build_postfix_snippet_builder<'ctx>(
     ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
         move |label, detail, snippet| {
             let edit = TextEdit::replace(delete_range, snippet.to_owned());
-            let mut item =
-                CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label);
+            let mut item = CompletionItem::new(CompletionItemKind::Snippet, delete_range, label);
             item.detail(detail).snippet_edit(cap, edit);
             let postfix_match = if ctx.original_token.text() == label {
                 cov_mark::hit!(postfix_exact_match_is_high_priority);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 5890af17eb..80f024184a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -10,7 +10,7 @@
 use std::{fmt, iter, ops::Not};
 
 use cfg::{CfgAtom, CfgDiff};
-use flycheck::{CargoOptions, FlycheckConfig};
+use flycheck::FlycheckConfig;
 use ide::{
     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
     HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
@@ -1364,22 +1364,6 @@ impl Config {
         self.data.check_workspace
     }
 
-    pub fn cargo_test_options(&self) -> CargoOptions {
-        CargoOptions {
-            target_triples: self.data.cargo_target.clone().into_iter().collect(),
-            all_targets: false,
-            no_default_features: self.data.cargo_noDefaultFeatures,
-            all_features: matches!(self.data.cargo_features, CargoFeaturesDef::All),
-            features: match self.data.cargo_features.clone() {
-                CargoFeaturesDef::All => vec![],
-                CargoFeaturesDef::Selected(it) => it,
-            },
-            extra_args: self.extra_args().clone(),
-            extra_env: self.extra_env().clone(),
-            target_dir: self.target_dir_from_config(),
-        }
-    }
-
     pub fn flycheck(&self) -> FlycheckConfig {
         match &self.data.check_overrideCommand {
             Some(args) if !args.is_empty() => {
@@ -1405,39 +1389,37 @@ impl Config {
             }
             Some(_) | None => FlycheckConfig::CargoCommand {
                 command: self.data.check_command.clone(),
-                options: CargoOptions {
-                    target_triples: self
-                        .data
-                        .check_targets
-                        .clone()
-                        .and_then(|targets| match &targets.0[..] {
-                            [] => None,
-                            targets => Some(targets.into()),
-                        })
-                        .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
-                    all_targets: self.data.check_allTargets.unwrap_or(self.data.cargo_allTargets),
-                    no_default_features: self
-                        .data
-                        .check_noDefaultFeatures
-                        .unwrap_or(self.data.cargo_noDefaultFeatures),
-                    all_features: matches!(
-                        self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
-                        CargoFeaturesDef::All
-                    ),
-                    features: match self
-                        .data
-                        .check_features
-                        .clone()
-                        .unwrap_or_else(|| self.data.cargo_features.clone())
-                    {
-                        CargoFeaturesDef::All => vec![],
-                        CargoFeaturesDef::Selected(it) => it,
-                    },
-                    extra_args: self.check_extra_args(),
-                    extra_env: self.check_extra_env(),
-                    target_dir: self.target_dir_from_config(),
+                target_triples: self
+                    .data
+                    .check_targets
+                    .clone()
+                    .and_then(|targets| match &targets.0[..] {
+                        [] => None,
+                        targets => Some(targets.into()),
+                    })
+                    .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
+                all_targets: self.data.check_allTargets.unwrap_or(self.data.cargo_allTargets),
+                no_default_features: self
+                    .data
+                    .check_noDefaultFeatures
+                    .unwrap_or(self.data.cargo_noDefaultFeatures),
+                all_features: matches!(
+                    self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
+                    CargoFeaturesDef::All
+                ),
+                features: match self
+                    .data
+                    .check_features
+                    .clone()
+                    .unwrap_or_else(|| self.data.cargo_features.clone())
+                {
+                    CargoFeaturesDef::All => vec![],
+                    CargoFeaturesDef::Selected(it) => it,
                 },
+                extra_args: self.check_extra_args(),
+                extra_env: self.check_extra_env(),
                 ansi_color_output: self.color_diagnostic_output(),
+                target_dir: self.target_dir_from_config(),
             },
         }
     }
@@ -2790,7 +2772,7 @@ mod tests {
             .unwrap();
         assert_eq!(config.data.cargo_targetDir, None);
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir.is_none())
         );
     }
 
@@ -2809,7 +2791,7 @@ mod tests {
             .unwrap();
         assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
         );
     }
 
@@ -2831,7 +2813,7 @@ mod tests {
             Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
         );
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder")))
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder")))
         );
     }
 }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index a0101af309..8516ffa0df 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -86,10 +86,7 @@ pub(crate) struct GlobalState {
     pub(crate) last_flycheck_error: Option<String>,
 
     // Test explorer
-    pub(crate) test_run_session: Option<Vec<flycheck::CargoTestHandle>>,
-    pub(crate) test_run_sender: Sender<flycheck::CargoTestMessage>,
-    pub(crate) test_run_receiver: Receiver<flycheck::CargoTestMessage>,
-    pub(crate) test_run_remaining_jobs: usize,
+    pub(crate) test_run_session: Option<flycheck::CargoTestHandle>,
 
     // VFS
     pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
@@ -194,7 +191,6 @@ impl GlobalState {
             analysis_host.update_lru_capacities(capacities);
         }
         let (flycheck_sender, flycheck_receiver) = unbounded();
-        let (test_run_sender, test_run_receiver) = unbounded();
         let mut this = GlobalState {
             sender,
             req_queue: ReqQueue::default(),
@@ -223,9 +219,6 @@ impl GlobalState {
             last_flycheck_error: None,
 
             test_run_session: None,
-            test_run_sender,
-            test_run_receiver,
-            test_run_remaining_jobs: 0,
 
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
             vfs_config_version: 0,
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index e5c070bf77..77692ed3ae 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -219,28 +219,14 @@ pub(crate) fn handle_run_test(
             .unwrap_or_default(),
         None => "".to_owned(),
     };
-    let test_path = if lca.is_empty() {
-        None
+    let handle = if lca.is_empty() {
+        flycheck::CargoTestHandle::new(None)
     } else if let Some((_, path)) = lca.split_once("::") {
-        Some(path)
+        flycheck::CargoTestHandle::new(Some(path))
     } else {
-        None
+        flycheck::CargoTestHandle::new(None)
     };
-    let mut handles = vec![];
-    for ws in &*state.workspaces {
-        if let ProjectWorkspace::Cargo { cargo, .. } = ws {
-            let handle = flycheck::CargoTestHandle::new(
-                test_path,
-                state.config.cargo_test_options(),
-                cargo.workspace_root(),
-                state.test_run_sender.clone(),
-            )?;
-            handles.push(handle);
-        }
-    }
-    // Each process send finished signal twice, once for stdout and once for stderr
-    state.test_run_remaining_jobs = 2 * handles.len();
-    state.test_run_session = Some(handles);
+    state.test_run_session = Some(handle?);
     Ok(())
 }
 
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ac270bc3c5..9459bd7c5d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -7,7 +7,7 @@ use std::{
 };
 
 use always_assert::always;
-use crossbeam_channel::{select, Receiver};
+use crossbeam_channel::{never, select, Receiver};
 use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
@@ -220,7 +220,7 @@ impl GlobalState {
             recv(self.flycheck_receiver) -> task =>
                 Some(Event::Flycheck(task.unwrap())),
 
-            recv(self.test_run_receiver) -> task =>
+            recv(self.test_run_session.as_ref().map(|s| s.receiver()).unwrap_or(&never())) -> task =>
                 Some(Event::TestResult(task.unwrap())),
 
         }
@@ -337,7 +337,9 @@ impl GlobalState {
                         .entered();
                 self.handle_cargo_test_msg(message);
                 // Coalesce many test result event into a single loop turn
-                while let Ok(message) = self.test_run_receiver.try_recv() {
+                while let Some(message) =
+                    self.test_run_session.as_ref().and_then(|r| r.receiver().try_recv().ok())
+                {
                     self.handle_cargo_test_msg(message);
                 }
             }
@@ -790,11 +792,8 @@ impl GlobalState {
             }
             flycheck::CargoTestMessage::Suite => (),
             flycheck::CargoTestMessage::Finished => {
-                self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
-                if self.test_run_remaining_jobs == 0 {
-                    self.send_notification::<lsp_ext::EndRunTest>(());
-                    self.test_run_session = None;
-                }
+                self.send_notification::<lsp_ext::EndRunTest>(());
+                self.test_run_session = None;
             }
             flycheck::CargoTestMessage::Custom { text } => {
                 self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index d7d283c3eb..7eeb10d544 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -132,10 +132,6 @@ impl FileSetConfig {
     ///
     /// `scratch_space` is used as a buffer and will be entirely replaced.
     fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize {
-        // `path` is a file, but r-a only cares about the containing directory. We don't
-        // want `/foo/bar_baz.rs` to be attributed to source root directory `/foo/bar`.
-        let path = path.parent().unwrap_or_else(|| path.clone());
-
         scratch_space.clear();
         path.encode(scratch_space);
         let automaton = PrefixOf::new(scratch_space.as_slice());
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 6521b6d8b3..8bc11fd481 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -210,10 +210,7 @@ While the sandbox can be disabled for some directories, `/usr/bin` will always b
 This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to.
 Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`.
 
-If you use a Flatpak SDK for Rust, it must be in your `PATH`:
-
- * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08`
- * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`)
+If you use a Flatpak SDK for Rust, there should be no extra steps necessary.
 
 If you want to use Flatpak in combination with `rustup`, the following steps might help:
 
diff --git a/editors/code/language-configuration.json b/editors/code/language-configuration.json
index 6619d0c85c..bdae0e6ba9 100644
--- a/editors/code/language-configuration.json
+++ b/editors/code/language-configuration.json
@@ -19,8 +19,7 @@
         { "open": "(", "close": ")" },
         { "open": "\"", "close": "\"", "notIn": ["string"] },
         { "open": "/*", "close": " */", "notIn": ["string"] },
-        { "open": "`", "close": "`", "notIn": ["string"] },
-        { "open": "```", "close": "```", "notIn": ["string"] }
+        { "open": "`", "close": "`", "notIn": ["string"] }
     ],
     "autoCloseBefore": ";:.,=}])> \n\t",
     "surroundingPairs": [
@@ -30,8 +29,7 @@
         ["<", ">"],
         ["\"", "\""],
         ["'", "'"],
-        ["`", "`"],
-        ["```", "```"]
+        ["`", "`"]
     ],
     "indentationRules": {
         "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 1cbf247297..372dc8bedf 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -131,7 +131,10 @@ export async function createClient(
                             ? diag.code
                             : diag.code?.value;
                     if (
-                        value === "unlinked-file" &&
+                        // FIXME: We currently emit this diagnostic way too early, before we have
+                        // loaded the project fully
+                        // value === "unlinked-file" &&
+                        value === "temporary-disabled" &&
                         !unlinkedFiles.includes(uri) &&
                         diag.message !== "file not included in module tree"
                     ) {


Test patch:
diff --git a/crates/flycheck/src/test_runner.rs b/crates/flycheck/src/test_runner.rs
index c136dd1366..9f761c9ead 100644
--- a/crates/flycheck/src/test_runner.rs
+++ b/crates/flycheck/src/test_runner.rs
@@ -3,15 +3,11 @@
 
 use std::process::Command;
 
-use crossbeam_channel::Sender;
-use paths::AbsPath;
+use crossbeam_channel::Receiver;
 use serde::Deserialize;
 use toolchain::Tool;
 
-use crate::{
-    command::{CommandHandle, ParseFromLine},
-    CargoOptions,
-};
+use crate::command::{CommandHandle, ParseFromLine};
 
 #[derive(Debug, Deserialize)]
 #[serde(tag = "event", rename_all = "camelCase")]
@@ -55,34 +51,30 @@ impl ParseFromLine for CargoTestMessage {
 
 #[derive(Debug)]
 pub struct CargoTestHandle {
-    _handle: CommandHandle<CargoTestMessage>,
+    handle: CommandHandle<CargoTestMessage>,
 }
 
 // Example of a cargo test command:
 // cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
 
 impl CargoTestHandle {
-    pub fn new(
-        path: Option<&str>,
-        options: CargoOptions,
-        root: &AbsPath,
-        sender: Sender<CargoTestMessage>,
-    ) -> std::io::Result<Self> {
+    pub fn new(path: Option<&str>) -> std::io::Result<Self> {
         let mut cmd = Command::new(Tool::Cargo.path());
         cmd.env("RUSTC_BOOTSTRAP", "1");
         cmd.arg("test");
         cmd.arg("--workspace");
         // --no-fail-fast is needed to ensure that all requested tests will run
         cmd.arg("--no-fail-fast");
-        cmd.arg("--manifest-path");
-        cmd.arg(root.join("Cargo.toml"));
-        options.apply_on_command(&mut cmd);
         cmd.arg("--");
         if let Some(path) = path {
             cmd.arg(path);
         }
         cmd.args(["-Z", "unstable-options"]);
         cmd.arg("--format=json");
-        Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? })
+        Ok(Self { handle: CommandHandle::spawn(cmd)? })
+    }
+
+    pub fn receiver(&self) -> &Receiver<CargoTestMessage> {
+        &self.handle.receiver
     }
 }
diff --git a/crates/vfs/src/file_set/tests.rs b/crates/vfs/src/file_set/tests.rs
index 3cdb60dcb2..2146df185d 100644
--- a/crates/vfs/src/file_set/tests.rs
+++ b/crates/vfs/src/file_set/tests.rs
@@ -40,26 +40,3 @@ fn name_prefix() {
     let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
     assert_eq!(partition, vec![1, 1, 0]);
 }
-
-/// Ensure that we don't consider `/foo/bar_baz.rs` to be in the
-/// `/foo/bar/` root.
-#[test]
-fn name_prefix_partially_matches() {
-    let mut file_set = FileSetConfig::builder();
-    file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
-    file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar".into())]);
-    let file_set = file_set.build();
-
-    let mut vfs = Vfs::default();
-
-    // These two are both in /foo.
-    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/lib.rs".into()), Some(Vec::new()));
-    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar_baz.rs".into()), Some(Vec::new()));
-
-    // Only this file is in /foo/bar.
-    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar/biz.rs".into()), Some(Vec::new()));
-
-    let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
-
-    assert_eq!(partition, vec![2, 1, 0]);
-}