Repository: rust-lang/rust-analyzer
Pull request #17442 (2024-06-17 17:43:14)

fix: Fix pat fragment parsers choking on <eoi>
Fixes https://github.com/rust-lang/rust-analyzer/issues/17441


Patch:
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index eadb2e19d1..f83e266744 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -91,6 +91,7 @@ pub(crate) fn fixup_syntax(
             preorder.skip_subtree();
             continue;
         }
+
         // In some other situations, we can fix things by just appending some tokens.
         match_ast! {
             match node {
@@ -275,62 +276,6 @@ pub(crate) fn fixup_syntax(
                         ]);
                     }
                 },
-                ast::RecordExprField(it) => {
-                    if let Some(colon) = it.colon_token() {
-                        if it.name_ref().is_some() {
-                            append.insert(colon.into(), vec![
-                                Leaf::Ident(Ident {
-                                    text: "__ra_fixup".into(),
-                                    span: fake_span(node_range)
-                                })
-                            ]);
-                        }
-                    }
-                },
-                ast::Path(it) => {
-                    if let Some(colon) = it.coloncolon_token() {
-                        if it.segment().is_none() {
-                            append.insert(colon.into(), vec![
-                                Leaf::Ident(Ident {
-                                    text: "__ra_fixup".into(),
-                                    span: fake_span(node_range)
-                                })
-                            ]);
-                        }
-                    }
-                },
-                ast::ArgList(it) => {
-                    if it.r_paren_token().is_none() {
-                        append.insert(node.into(), vec![
-                            Leaf::Punct(Punct {
-                                span: fake_span(node_range),
-                                char: ')',
-                                spacing: Spacing::Alone
-                            })
-                        ]);
-                    }
-                },
-                ast::ArgList(it) => {
-                    if it.r_paren_token().is_none() {
-                        append.insert(node.into(), vec![
-                            Leaf::Punct(Punct {
-                                span: fake_span(node_range),
-                                char: ')',
-                                spacing: Spacing::Alone
-                            })
-                        ]);
-                    }
-                },
-                ast::ClosureExpr(it) => {
-                    if it.body().is_none() {
-                        append.insert(node.into(), vec![
-                            Leaf::Ident(Ident {
-                                text: "__ra_fixup".into(),
-                                span: fake_span(node_range)
-                            })
-                        ]);
-                    }
-                },
                 _ => (),
             }
         }
@@ -814,70 +759,4 @@ fn foo () {loop { }}
 "#]],
         )
     }
-
-    #[test]
-    fn fixup_path() {
-        check(
-            r#"
-fn foo() {
-    path::
-}
-"#,
-            expect![[r#"
-fn foo () {path :: __ra_fixup}
-"#]],
-        )
-    }
-
-    #[test]
-    fn fixup_record_ctor_field() {
-        check(
-            r#"
-fn foo() {
-    R { f: }
-}
-"#,
-            expect![[r#"
-fn foo () {R {f : __ra_fixup}}
-"#]],
-        )
-    }
-
-    #[test]
-    fn fixup_arg_list() {
-        check(
-            r#"
-fn foo() {
-    foo(a
-}
-"#,
-            expect![[r#"
-fn foo () { foo ( a ) }
-"#]],
-        );
-        check(
-            r#"
-fn foo() {
-    bar.foo(a
-}
-"#,
-            expect![[r#"
-fn foo () { bar . foo ( a ) }
-"#]],
-        );
-    }
-
-    #[test]
-    fn fixup_closure() {
-        check(
-            r#"
-fn foo() {
-    ||
-}
-"#,
-            expect![[r#"
-fn foo () {|| __ra_fixup}
-"#]],
-        );
-    }
 }
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index 62104fb7dc..b4486d1502 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -11,9 +11,9 @@ use hir::db::DefDatabase;
 use crate::{
     base_db::{
         salsa::{Database, ParallelDatabase, Snapshot},
-        Cancelled, CrateId, SourceDatabase, SourceDatabaseExt,
+        Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
     },
-    FxIndexMap, RootDatabase,
+    FxHashSet, FxIndexMap, RootDatabase,
 };
 
 /// We're indexing many crates.
@@ -36,10 +36,19 @@ pub fn parallel_prime_caches(
 
     let graph = db.crate_graph();
     let mut crates_to_prime = {
+        let crate_ids = compute_crates_to_prime(db, &graph);
+
         let mut builder = topologic_sort::TopologicalSortIter::builder();
 
-        for crate_id in graph.iter() {
-            builder.add(crate_id, graph[crate_id].dependencies.iter().map(|d| d.crate_id));
+        for &crate_id in &crate_ids {
+            let crate_data = &graph[crate_id];
+            let dependencies = crate_data
+                .dependencies
+                .iter()
+                .map(|d| d.crate_id)
+                .filter(|i| crate_ids.contains(i));
+
+            builder.add(crate_id, dependencies);
         }
 
         builder.build()
@@ -53,20 +62,13 @@ pub fn parallel_prime_caches(
     let (work_sender, progress_receiver) = {
         let (progress_sender, progress_receiver) = crossbeam_channel::unbounded();
         let (work_sender, work_receiver) = crossbeam_channel::unbounded();
-        let graph = graph.clone();
         let prime_caches_worker = move |db: Snapshot<RootDatabase>| {
             while let Ok((crate_id, crate_name)) = work_receiver.recv() {
                 progress_sender
                     .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
 
-                let file_id = graph[crate_id].root_file_id;
-                let root_id = db.file_source_root(file_id);
-                if db.source_root(root_id).is_library {
-                    db.crate_def_map(crate_id);
-                } else {
-                    // This also computes the DefMap
-                    db.import_map(crate_id);
-                }
+                // This also computes the DefMap
+                db.import_map(crate_id);
 
                 progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
             }
@@ -74,13 +76,13 @@ pub fn parallel_prime_caches(
             Ok::<_, crossbeam_channel::SendError<_>>(())
         };
 
-        for id in 0..num_worker_threads {
+        for _ in 0..num_worker_threads {
             let worker = prime_caches_worker.clone();
             let db = db.snapshot();
 
             stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
                 .allow_leak(true)
-                .name(format!("PrimeCaches#{id}"))
+                .name("PrimeCaches".to_owned())
                 .spawn(move || Cancelled::catch(|| worker(db)))
                 .expect("failed to spawn thread");
         }
@@ -94,7 +96,7 @@ pub fn parallel_prime_caches(
     // an index map is used to preserve ordering so we can sort the progress report in order of
     // "longest crate to index" first
     let mut crates_currently_indexing =
-        FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default());
+        FxIndexMap::with_capacity_and_hasher(num_worker_threads as _, Default::default());
 
     while crates_done < crates_total {
         db.unwind_if_cancelled();
@@ -142,3 +144,19 @@ pub fn parallel_prime_caches(
         cb(progress);
     }
 }
+
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
+    // We're only interested in the workspace crates and the `ImportMap`s of their direct
+    // dependencies, though in practice the latter also compute the `DefMap`s.
+    // We don't prime transitive dependencies because they're generally not visible in
+    // the current workspace.
+    graph
+        .iter()
+        .filter(|&id| {
+            let file_id = graph[id].root_file_id;
+            let root_id = db.file_source_root(file_id);
+            !db.source_root(root_id).is_library
+        })
+        .flat_map(|id| graph[id].dependencies.iter().map(|krate| krate.crate_id))
+        .collect()
+}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 3f10bed511..e9c48ab1e7 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -455,23 +455,6 @@ fn ty_to_text_edit(
 // * elided lifetimes
 // * compiler inserted reborrows
 //
-// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
-// any of the
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
-// are met:
-//
-// * the parameter name is a suffix of the function's name
-// * the argument is a qualified constructing or call expression where the qualifier is an ADT
-// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
-//   of argument with _ splitting it off
-// * the parameter name starts with `ra_fixture`
-// * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
-// in a unary function
-// * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
-// in a unary function
-//
 // image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index eff6b66404..882c243b0c 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -181,7 +181,7 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
                 //       ^
                 if matches!(
                     p.current(),
-                    T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']'] | T![if]
+                    T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']'] | T![if] | EOF
                 ) {
                     // test half_open_range_pat
                     // fn f() {
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 3bc739e934..8e6c53d0c5 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -21,7 +21,7 @@ The LSP allows various code editors, like VS Code, Emacs or Vim, to implement se
 To improve this document, send a pull request: +
 https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc]
 
-The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy.
+The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo test -p xtask` to create these and then `asciidoctor manual.adoc` to create an HTML copy.
 ====
 
 If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum.


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index c5c26e26bc..4058159cef 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1883,3 +1883,41 @@ fn test() {
 "#]],
     );
 }
+
+#[test]
+fn test_pat_fragment_eof_17441() {
+    check(
+        r#"
+macro_rules! matches {
+    ($expression:expr, $pattern:pat $(if $guard:expr)? ) => {
+        match $expression {
+            $pattern $(if $guard)? => true,
+            _ => false
+        }
+    };
+}
+fn f() {
+    matches!(0, 10..);
+    matches!(0, 10.. if true);
+}
+ "#,
+        expect![[r#"
+macro_rules! matches {
+    ($expression:expr, $pattern:pat $(if $guard:expr)? ) => {
+        match $expression {
+            $pattern $(if $guard)? => true,
+            _ => false
+        }
+    };
+}
+fn f() {
+    match 0 {
+        10.. =>true , _=>false
+    };
+    match 0 {
+        10..if true =>true , _=>false
+    };
+}
+ "#]],
+    );
+}