Repository: rust-lang/rust-analyzer
Pull request #19062 (2025-01-28 15:36:09)

Fix scip indexing of module names
* Fix an overflow in computing `is_inherent_impl` (`moniker.identifier.description.len() - 2`). No behavior change, just annoying when trying to debug.
* Fix missing symbol name for occurrences of `crate`. Where we previously emitted, e.g., `{ "range": [ 5, 4, 9 ] }`, we will now emit `{ "range": [ 5, 4, 9 ], "symbol": "rust-analyzer cargo foo . crate/" }`.
* Fix missing symbols for implicit module names. We previously emitted occurrences of module names but not the actual symbol definitions for modules implicitly defined by files.




Patch:
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index e0e366f450..62feca5f8c 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -194,11 +194,7 @@ pub(crate) fn deref_by_trait(
     }
 
     let trait_id = || {
-        // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will
-        // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the
-        // blanked impl on `Deref`.
-        #[expect(clippy::overly_complex_bool_expr)]
-        if use_receiver_trait && false {
+        if use_receiver_trait {
             if let Some(receiver) =
                 db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
             {
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 2aca99c93b..f655692429 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -240,15 +240,9 @@ impl<'a> UnsafeVisitor<'a> {
         let inside_assignment = mem::replace(&mut self.inside_assignment, false);
         match expr {
             &Expr::Call { callee, .. } => {
-                let callee = &self.infer[callee];
-                if let Some(func) = callee.as_fn_def(self.db) {
+                if let Some(func) = self.infer[callee].as_fn_def(self.db) {
                     self.check_call(current, func);
                 }
-                if let TyKind::Function(fn_ptr) = callee.kind(Interner) {
-                    if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe {
-                        self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall);
-                    }
-                }
             }
             Expr::Path(path) => {
                 let guard =
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index a38c0211e8..b414ef0d71 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4730,6 +4730,17 @@ impl Type {
         Type { env: TraitEnvironment::empty(krate), ty }
     }
 
+    pub fn reference(inner: &Type, m: Mutability) -> Type {
+        inner.derived(
+            TyKind::Ref(
+                if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
+                hir_ty::error_lifetime(),
+                inner.ty.clone(),
+            )
+            .intern(Interner),
+        )
+    }
+
     fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
         let resolver = lexical_env.resolver(db.upcast());
         let environment = resolver
@@ -4961,17 +4972,6 @@ impl Type {
         self.normalize_trait_assoc_type(db, &[], iterator_item.into())
     }
 
-    pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool {
-        let Some(iterator_trait) =
-            db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait())
-        else {
-            return false;
-        };
-        let canonical_ty =
-            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
-        method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, iterator_trait)
-    }
-
     /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
     pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
         let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs
index af72179305..6f84513708 100644
--- a/crates/hir/src/term_search.rs
+++ b/crates/hir/src/term_search.rs
@@ -145,7 +145,7 @@ impl LookupTable {
                 self.data
                     .iter()
                     .find(|(t, _)| {
-                        t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty)
+                        Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty)
                     })
                     .map(|(t, it)| {
                         it.exprs(t)
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index c1332d99bf..78ff441791 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -793,8 +793,8 @@ pub(crate) fn convert_reference_type(
 }
 
 fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool {
-    let ty_ref = ty.add_reference(hir::Mutability::Shared);
-    let target_ref = target.add_reference(hir::Mutability::Shared);
+    let ty_ref = hir::Type::reference(ty, hir::Mutability::Shared);
+    let target_ref = hir::Type::reference(target, hir::Mutability::Shared);
     ty_ref.could_coerce_to(db, &target_ref)
 }
 
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index b38b9ac1f5..d12654665c 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -89,7 +89,7 @@ pub(crate) fn complete_dot(
         acc.add_method(ctx, dot_access, func, None, None)
     });
 
-    if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) {
+    if ctx.config.enable_auto_iter {
         // FIXME:
         // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
         // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
@@ -1499,32 +1499,10 @@ fn main() {
     let bar = Bar;
     bar.$0
 }
-"#,
-            expect![[r#""#]],
-        );
-    }
-
-    #[test]
-    fn no_iter_suggestion_on_iterator() {
-        check_no_kw(
-            r#"
-//- minicore: iterator
-struct MyIter;
-impl Iterator for MyIter {
-    type Item = ();
-    fn next(&mut self) -> Option<Self::Item> { None }
-}
-
-fn main() {
-    MyIter.$0
-}
 "#,
             expect![[r#"
-                me by_ref() (as Iterator)                             fn(&mut self) -> &mut Self
-                me into_iter() (as IntoIterator)    fn(self) -> <Self as IntoIterator>::IntoIter
-                me next() (as Iterator)        fn(&mut self) -> Option<<Self as Iterator>::Item>
-                me nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
-            "#]],
+    me foo() fn(self: Bar)
+"#]],
         );
     }
 }
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 323a5723d4..d48464769c 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -862,18 +862,6 @@ fn bar() {
 fn baz() {
     foo();
  // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn unsafe_fn_ptr_call() {
-        check_diagnostics(
-            r#"
-fn f(it: unsafe fn()){
-    it();
- // ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
 }
         "#,
         );
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4080b5f12e..7bc1be3822 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,5 @@
 use either::Either;
-use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile};
+use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
 use ide_db::{
     famous_defs::FamousDefs,
     source_change::{SourceChange, SourceChangeBuilder},
@@ -88,7 +88,7 @@ fn add_reference(
     let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into()));
 
     let (_, mutability) = d.expected.as_reference()?;
-    let actual_with_ref = d.actual.add_reference(mutability);
+    let actual_with_ref = Type::reference(&d.actual, mutability);
     if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
         return None;
     }
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index d97c12ebaf..66ea49a98a 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -289,7 +289,10 @@ fn def_to_non_local_moniker(
     definition: Definition,
     from_crate: Crate,
 ) -> Option<Moniker> {
-    let module = definition.module(db)?;
+    let module = match definition {
+        Definition::Module(module) if module.is_crate_root() => module,
+        _ => definition.module(db)?,
+    };
     let krate = module.krate();
     let edition = krate.edition(db);
 
@@ -322,12 +325,18 @@ fn def_to_non_local_moniker(
                         name: name.display(db, edition).to_string(),
                         desc: def_to_kind(db, def).into(),
                     });
-                } else if reverse_description.is_empty() {
-                    // Don't allow the last descriptor to be absent.
-                    return None;
                 } else {
                     match def {
-                        Definition::Module(module) if module.is_crate_root() => {}
+                        Definition::Module(module) if module.is_crate_root() => {
+                            // only include `crate` namespace by itself because we prefer
+                            // `rust-analyzer cargo foo . bar/` over `rust-analyzer cargo foo . crate/bar/`
+                            if reverse_description.is_empty() {
+                                reverse_description.push(MonikerDescriptor {
+                                    name: "crate".to_owned(),
+                                    desc: MonikerDescriptorKind::Namespace,
+                                });
+                            }
+                        }
                         _ => {
                             tracing::error!(?def, "Encountered enclosing definition with no name");
                         }
@@ -340,6 +349,9 @@ fn def_to_non_local_moniker(
         };
         def = next_def;
     }
+    if reverse_description.is_empty() {
+        return None;
+    }
     reverse_description.reverse();
     let description = reverse_description;
 
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 8050a38b3c..3121bb7a8e 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -169,10 +169,10 @@ impl StaticIndex<'_> {
             .unwrap();
         // hovers
         let sema = hir::Semantics::new(self.db);
-        let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone();
+        let root = sema.parse_guess_edition(file_id).syntax().clone();
         let edition =
             sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
-        let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it {
+        let tokens = root.descendants_with_tokens().filter_map(|it| match it {
             syntax::NodeOrToken::Node(_) => None,
             syntax::NodeOrToken::Token(it) => Some(it),
         });
@@ -194,24 +194,19 @@ impl StaticIndex<'_> {
             )
         });
         let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] };
-        for token in tokens {
-            let range = token.text_range();
-            let node = token.parent().unwrap();
-            let def = match get_definition(&sema, token.clone()) {
-                Some(it) => it,
-                None => continue,
-            };
+
+        let mut add_token = |def: Definition, range: TextRange, scope_node: &SyntaxNode| {
             let id = if let Some(it) = self.def_map.get(&def) {
                 *it
             } else {
                 let it = self.tokens.insert(TokenStaticData {
-                    documentation: documentation_for_definition(&sema, def, &node),
+                    documentation: documentation_for_definition(&sema, def, scope_node),
                     hover: Some(hover_for_definition(
                         &sema,
                         file_id,
                         def,
                         None,
-                        &node,
+                        scope_node,
                         None,
                         false,
                         &hover_config,
@@ -240,6 +235,22 @@ impl StaticIndex<'_> {
                 },
             });
             result.tokens.push((range, id));
+        };
+
+        if let Some(module) = sema.file_to_module_def(file_id) {
+            let def = Definition::Module(module);
+            let range = root.text_range();
+            add_token(def, range, &root);
+        }
+
+        for token in tokens {
+            let range = token.text_range();
+            let node = token.parent().unwrap();
+            let def = match get_definition(&sema, token.clone()) {
+                Some(it) => it,
+                None => continue,
+            };
+            add_token(def, range, &node);
         }
         self.files.push(result);
     }
@@ -300,6 +311,10 @@ mod tests {
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
         for f in s.files {
             for (range, _) in f.tokens {
+                if range.start() == TextSize::from(0) {
+                    // ignore whole file range corresponding to module definition
+                    continue;
+                }
                 let it = FileRange { file_id: f.file_id, range };
                 if !range_set.contains(&it) {
                     panic!("additional range {it:?}");
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index dc0f722aae..ee7b698975 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -444,14 +444,14 @@ impl SymbolGenerator {
                     MonikerResult::Moniker(moniker) => TokenSymbols {
                         symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)),
                         enclosing_symbol: None,
-                        is_inherent_impl: moniker
-                            .identifier
-                            .description
-                            .get(moniker.identifier.description.len() - 2)
-                            .is_some_and(|descriptor| {
+                        is_inherent_impl: match &moniker.identifier.description[..] {
+                            // inherent impls are represented as impl#[SelfType]
+                            [.., descriptor, _] => {
                                 descriptor.desc == MonikerDescriptorKind::Type
                                     && descriptor.name == "impl"
-                            }),
+                            }
+                            _ => false,
+                        },
                     },
                     MonikerResult::Local { enclosing_moniker } => {
                         let local_symbol = scip::types::Symbol::new_local(local_count);
@@ -549,7 +549,9 @@ mod test {
                 continue;
             }
             for &(range, id) in &file.tokens {
-                if range.contains(offset - TextSize::from(1)) {
+                // check if cursor is within token, ignoring token for the module defined by the file (whose range is the whole file)
+                if range.start() != TextSize::from(0) && range.contains(offset - TextSize::from(1))
+                {
                     let token = si.tokens.get(id).unwrap();
                     found_symbol = match token.moniker.as_ref() {
                         None => None,
@@ -885,7 +887,7 @@ pub mod example_mod {
         );
 
         let file = si.files.first().unwrap();
-        let (_, token_id) = file.tokens.first().unwrap();
+        let (_, token_id) = file.tokens.get(1).unwrap(); // first token is file module, second is `bar`
         let token = si.tokens.get(*token_id).unwrap();
 
         assert_eq!(token.documentation.as_ref().map(|d| d.as_str()), Some("foo"));
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index 4683877db6..ff50f7533a 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -118,7 +118,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -147,7 +147,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<false, false, R>(ThreadIntent::Worker, f, on_cancelled)
+        self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled)
     }
 
     /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -166,7 +166,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -193,7 +193,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -212,7 +212,7 @@ impl RequestDispatcher<'_> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::<true, false, R>(
+        self.on_with_thread_intent::<false, false, R>(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -231,7 +231,7 @@ impl RequestDispatcher<'_> {
         }
     }
 
-    fn on_with_thread_intent<const RUSTFMT: bool, const ALLOW_RETRYING: bool, R>(
+    fn on_with_thread_intent<const MAIN_POOL: bool, const ALLOW_RETRYING: bool, R>(
         &mut self,
         intent: ThreadIntent,
         f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
@@ -251,10 +251,10 @@ impl RequestDispatcher<'_> {
         tracing::debug!(?params);
 
         let world = self.global_state.snapshot();
-        if RUSTFMT {
-            &mut self.global_state.fmt_pool.handle
-        } else {
+        if MAIN_POOL {
             &mut self.global_state.task_pool.handle
+        } else {
+            &mut self.global_state.fmt_pool.handle
         }
         .spawn(intent, move || {
             let result = panic::catch_unwind(move || {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index de0ee639ca..d6dc8b521f 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -253,11 +253,6 @@ impl GlobalState {
         &self,
         inbox: &Receiver<lsp_server::Message>,
     ) -> Result<Option<Event>, crossbeam_channel::RecvError> {
-        // Make sure we reply to formatting requests ASAP so the editor doesn't block
-        if let Ok(task) = self.fmt_pool.receiver.try_recv() {
-            return Ok(Some(Event::Task(task)));
-        }
-
         select! {
             recv(inbox) -> msg =>
                 return Ok(msg.ok().map(Event::Lsp)),


Test patch:
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index e5f791ea6f..8866de22df 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -2163,9 +2163,9 @@ impl Receiver for Bar {
 fn main() {
     let bar = Bar;
     let _v1 = bar.foo1();
-      //^^^ type: {unknown}
+      //^^^ type: i32
     let _v2 = bar.foo2();
-      //^^^ type: {unknown}
+      //^^^ type: bool
 }
 "#,
     );
diff --git a/crates/rust-analyzer/tests/slow-tests/cli.rs b/crates/rust-analyzer/tests/slow-tests/cli.rs
index fba5466691..4ef930e985 100644
--- a/crates/rust-analyzer/tests/slow-tests/cli.rs
+++ b/crates/rust-analyzer/tests/slow-tests/cli.rs
@@ -43,89 +43,93 @@ mod tests {
     expect![[r#"
         {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]}
         {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1}
-        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
+        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
         {"id":5,"type":"vertex","label":"resultSet"}
         {"id":6,"type":"edge","label":"next","inV":5,"outV":4}
-        {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":7,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
         {"id":8,"type":"vertex","label":"resultSet"}
         {"id":9,"type":"edge","label":"next","inV":8,"outV":7}
-        {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
-        {"id":11,"type":"edge","label":"next","inV":8,"outV":10}
-        {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
-        {"id":13,"type":"vertex","label":"resultSet"}
-        {"id":14,"type":"edge","label":"next","inV":13,"outV":12}
-        {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
+        {"id":10,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":11,"type":"vertex","label":"resultSet"}
+        {"id":12,"type":"edge","label":"next","inV":11,"outV":10}
+        {"id":13,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
+        {"id":14,"type":"edge","label":"next","inV":11,"outV":13}
+        {"id":15,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
         {"id":16,"type":"vertex","label":"resultSet"}
         {"id":17,"type":"edge","label":"next","inV":16,"outV":15}
-        {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":18,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
         {"id":19,"type":"vertex","label":"resultSet"}
         {"id":20,"type":"edge","label":"next","inV":19,"outV":18}
-        {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
-        {"id":22,"type":"edge","label":"next","inV":8,"outV":21}
-        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
-        {"id":24,"type":"vertex","label":"resultSet"}
-        {"id":25,"type":"edge","label":"next","inV":24,"outV":23}
-        {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1}
-        {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
-        {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5}
-        {"id":29,"type":"vertex","label":"referenceResult"}
-        {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5}
-        {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29}
-        {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
-        {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8}
-        {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
-        {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
-        {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35}
-        {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8}
-        {"id":38,"type":"vertex","label":"definitionResult"}
-        {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38}
-        {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8}
-        {"id":41,"type":"vertex","label":"referenceResult"}
-        {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8}
-        {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41}
-        {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41}
-        {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
-        {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13}
-        {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
-        {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47}
-        {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13}
-        {"id":50,"type":"vertex","label":"definitionResult"}
-        {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50}
-        {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13}
-        {"id":53,"type":"vertex","label":"referenceResult"}
-        {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13}
-        {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53}
-        {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
-        {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16}
-        {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
-        {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58}
-        {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16}
-        {"id":61,"type":"vertex","label":"definitionResult"}
-        {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61}
-        {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16}
-        {"id":64,"type":"vertex","label":"referenceResult"}
-        {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16}
-        {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64}
-        {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
-        {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19}
-        {"id":69,"type":"vertex","label":"definitionResult"}
-        {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
-        {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1}
-        {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69}
-        {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19}
-        {"id":74,"type":"vertex","label":"referenceResult"}
-        {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19}
-        {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74}
-        {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
-        {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24}
-        {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
-        {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79}
-        {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24}
-        {"id":82,"type":"vertex","label":"definitionResult"}
-        {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82}
-        {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24}
-        {"id":85,"type":"vertex","label":"referenceResult"}
-        {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24}
-        {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85}
+        {"id":21,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":22,"type":"edge","label":"next","inV":5,"outV":21}
+        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
+        {"id":24,"type":"edge","label":"next","inV":11,"outV":23}
+        {"id":25,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
+        {"id":26,"type":"vertex","label":"resultSet"}
+        {"id":27,"type":"edge","label":"next","inV":26,"outV":25}
+        {"id":28,"type":"edge","label":"contains","inVs":[4,7,10,13,15,18,21,23,25],"outV":1}
+        {"id":29,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
+        {"id":30,"type":"edge","label":"textDocument/hover","inV":29,"outV":5}
+        {"id":31,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
+        {"id":32,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::crate","unique":"scheme","kind":"export"}
+        {"id":33,"type":"edge","label":"packageInformation","inV":31,"outV":32}
+        {"id":34,"type":"edge","label":"moniker","inV":32,"outV":5}
+        {"id":35,"type":"vertex","label":"definitionResult"}
+        {"id":36,"type":"edge","label":"item","document":1,"inVs":[4],"outV":35}
+        {"id":37,"type":"edge","label":"textDocument/definition","inV":35,"outV":5}
+        {"id":38,"type":"vertex","label":"referenceResult"}
+        {"id":39,"type":"edge","label":"textDocument/references","inV":38,"outV":5}
+        {"id":40,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[4],"outV":38}
+        {"id":41,"type":"edge","label":"item","document":1,"property":"references","inVs":[21],"outV":38}
+        {"id":42,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
+        {"id":43,"type":"edge","label":"textDocument/hover","inV":42,"outV":8}
+        {"id":44,"type":"vertex","label":"referenceResult"}
+        {"id":45,"type":"edge","label":"textDocument/references","inV":44,"outV":8}
+        {"id":46,"type":"edge","label":"item","document":1,"property":"references","inVs":[7],"outV":44}
+        {"id":47,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
+        {"id":48,"type":"edge","label":"textDocument/hover","inV":47,"outV":11}
+        {"id":49,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
+        {"id":50,"type":"edge","label":"packageInformation","inV":31,"outV":49}
+        {"id":51,"type":"edge","label":"moniker","inV":49,"outV":11}
+        {"id":52,"type":"vertex","label":"definitionResult"}
+        {"id":53,"type":"edge","label":"item","document":1,"inVs":[10],"outV":52}
+        {"id":54,"type":"edge","label":"textDocument/definition","inV":52,"outV":11}
+        {"id":55,"type":"vertex","label":"referenceResult"}
+        {"id":56,"type":"edge","label":"textDocument/references","inV":55,"outV":11}
+        {"id":57,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[10],"outV":55}
+        {"id":58,"type":"edge","label":"item","document":1,"property":"references","inVs":[13,23],"outV":55}
+        {"id":59,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
+        {"id":60,"type":"edge","label":"textDocument/hover","inV":59,"outV":16}
+        {"id":61,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
+        {"id":62,"type":"edge","label":"packageInformation","inV":31,"outV":61}
+        {"id":63,"type":"edge","label":"moniker","inV":61,"outV":16}
+        {"id":64,"type":"vertex","label":"definitionResult"}
+        {"id":65,"type":"edge","label":"item","document":1,"inVs":[15],"outV":64}
+        {"id":66,"type":"edge","label":"textDocument/definition","inV":64,"outV":16}
+        {"id":67,"type":"vertex","label":"referenceResult"}
+        {"id":68,"type":"edge","label":"textDocument/references","inV":67,"outV":16}
+        {"id":69,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":67}
+        {"id":70,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
+        {"id":71,"type":"edge","label":"textDocument/hover","inV":70,"outV":19}
+        {"id":72,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
+        {"id":73,"type":"edge","label":"packageInformation","inV":31,"outV":72}
+        {"id":74,"type":"edge","label":"moniker","inV":72,"outV":19}
+        {"id":75,"type":"vertex","label":"definitionResult"}
+        {"id":76,"type":"edge","label":"item","document":1,"inVs":[18],"outV":75}
+        {"id":77,"type":"edge","label":"textDocument/definition","inV":75,"outV":19}
+        {"id":78,"type":"vertex","label":"referenceResult"}
+        {"id":79,"type":"edge","label":"textDocument/references","inV":78,"outV":19}
+        {"id":80,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[18],"outV":78}
+        {"id":81,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
+        {"id":82,"type":"edge","label":"textDocument/hover","inV":81,"outV":26}
+        {"id":83,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
+        {"id":84,"type":"edge","label":"packageInformation","inV":31,"outV":83}
+        {"id":85,"type":"edge","label":"moniker","inV":83,"outV":26}
+        {"id":86,"type":"vertex","label":"definitionResult"}
+        {"id":87,"type":"edge","label":"item","document":1,"inVs":[25],"outV":86}
+        {"id":88,"type":"edge","label":"textDocument/definition","inV":86,"outV":26}
+        {"id":89,"type":"vertex","label":"referenceResult"}
+        {"id":90,"type":"edge","label":"textDocument/references","inV":89,"outV":26}
+        {"id":91,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[25],"outV":89}
     "#]].assert_eq(stdout);
 }
diff --git a/crates/rust-analyzer/tests/slow-tests/testdir.rs b/crates/rust-analyzer/tests/slow-tests/testdir.rs
index 409be2894f..d113bd5127 100644
--- a/crates/rust-analyzer/tests/slow-tests/testdir.rs
+++ b/crates/rust-analyzer/tests/slow-tests/testdir.rs
@@ -43,15 +43,10 @@ impl TestDir {
             }
             fs::create_dir_all(&path).unwrap();
 
-            #[cfg(any(
-                target_os = "macos",
-                target_os = "linux",
-                target_os = "windows",
-                target_os = "freebsd"
-            ))]
+            #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
             if symlink {
                 let symlink_path = base.join(format!("{pid}_{cnt}_symlink"));
-                #[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))]
+                #[cfg(any(target_os = "macos", target_os = "linux"))]
                 std::os::unix::fs::symlink(path, &symlink_path).unwrap();
 
                 #[cfg(target_os = "windows")]