Repository: rust-lang/rust-analyzer
Pull request #13344 (2022-10-04 16:05:47)

fix: use `BoundVar`s from current generic scope
Fixup for #13335, addresses https://github.com/rust-lang/rust-analyzer/pull/13339#issuecomment-1266654607

Before the change in generic parameter order, `BoundVar`s for trait reference didn't change whether you are in an impl's scope or in an associated item's scope. Now that item's generic params come before its parent's, we need to shift their indices when we are in an associated item's scope.


Patch:
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index e8c63d410a..fdc03f4053 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -169,17 +169,13 @@ impl FlycheckActor {
     }
     fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
         let check_chan = self.cargo_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::Restart(msg));
-        }
         select! {
             recv(inbox) -> msg => msg.ok().map(Event::Restart),
             recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
     fn run(mut self, inbox: Receiver<Restart>) {
-        'event: while let Some(event) = self.next_event(&inbox) {
+        while let Some(event) = self.next_event(&inbox) {
             match event {
                 Event::Restart(Restart::No) => {
                     self.cancel_check_process();
@@ -187,12 +183,7 @@ impl FlycheckActor {
                 Event::Restart(Restart::Yes) => {
                     // Cancel the previously spawned process
                     self.cancel_check_process();
-                    while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
-                        // restart chained with a stop, so just cancel
-                        if let Restart::No = restart {
-                            continue 'event;
-                        }
-                    }
+                    while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}
 
                     let command = self.check_command();
                     tracing::debug!(?command, "will restart flycheck");
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index a77dd910ff..223d705b15 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1158,11 +1158,28 @@ fn named_associated_type_shorthand_candidates<R>(
     };
 
     match res {
-        TypeNs::SelfType(impl_id) => search(
+        TypeNs::SelfType(impl_id) => {
             // we're _in_ the impl -- the binders get added back later. Correct,
             // but it would be nice to make this more explicit
-            db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
-        ),
+            let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0;
+
+            let impl_id_as_generic_def: GenericDefId = impl_id.into();
+            if impl_id_as_generic_def != def {
+                // `trait_ref` contains `BoundVar`s bound by impl's `Binders`, but here we need
+                // `BoundVar`s from `def`'s point of view.
+                // FIXME: A `HirDatabase` query may be handy if this process is needed in more
+                // places. It'd be almost identical as `impl_trait_query` where `resolver` would be
+                // of `def` instead of `impl_id`.
+                let starting_idx = generics(db.upcast(), def).len_self();
+                let subst = TyBuilder::subst_for_def(db, impl_id, None)
+                    .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
+                    .build();
+                let trait_ref = subst.apply(trait_ref, Interner);
+                search(trait_ref)
+            } else {
+                search(trait_ref)
+            }
+        }
         TypeNs::GenericParam(param_id) => {
             let predicates = db.generic_predicates_for_param(def, param_id.into(), assoc_name);
             let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 72ddf80928..8c3e8681d9 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -717,6 +717,7 @@ fn cargo_to_crate_graph(
                 load_proc_macro,
                 &mut pkg_to_lib_crate,
                 &public_deps,
+                libproc_macro,
                 cargo,
                 &pkg_crates,
                 build_scripts,
@@ -782,6 +783,7 @@ fn handle_rustc_crates(
     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
     public_deps: &SysrootPublicDeps,
+    libproc_macro: Option<CrateId>,
     cargo: &CargoWorkspace,
     pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
     build_scripts: &WorkspaceBuildScripts,
@@ -843,6 +845,19 @@ fn handle_rustc_crates(
                         rustc_workspace[tgt].is_proc_macro,
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
+
+                    // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
+                    // (just none of the APIs work when called outside of a proc macro).
+                    if let Some(proc_macro) = libproc_macro {
+                        add_dep_with_prelude(
+                            crate_graph,
+                            crate_id,
+                            CrateName::new("proc_macro").unwrap(),
+                            proc_macro,
+                            cargo[tgt].is_proc_macro,
+                        );
+                    }
+
                     // Add dependencies on core / std / alloc for this crate
                     public_deps.add(crate_id, crate_graph);
                     rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);


Test patch:
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 16ba3dd6e3..a155adcec6 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -1694,3 +1694,16 @@ fn foo(a: &dyn DoesNotExist) {
         "#,
     );
 }
+
+#[test]
+fn self_assoc_with_const_generics_crash() {
+    check_no_mismatches(
+        r#"
+trait Trait { type Item; }
+impl<T, const N: usize> Trait for [T; N] {
+    type Item = ();
+    fn f<U>(_: Self::Item) {}
+}
+        "#,
+    );
+}