Repository: rust-lang/rust-analyzer
Pull request #17618 (2024-07-17 09:47:05)

Support rustc_skip_during_method_dispatch
Fixes https://github.com/rust-lang/rust-analyzer/issues/17256


Patch:
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index a887612707..37506b6cdc 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -223,6 +223,7 @@ pub struct TraitData {
     pub is_unsafe: bool,
     pub rustc_has_incoherent_inherent_impls: bool,
     pub skip_array_during_method_dispatch: bool,
+    pub skip_boxed_slice_during_method_dispatch: bool,
     pub fundamental: bool,
     pub visibility: RawVisibility,
     /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
@@ -250,8 +251,17 @@ impl TraitData {
         let is_unsafe = tr_def.is_unsafe;
         let visibility = item_tree[tr_def.visibility].clone();
         let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
-        let skip_array_during_method_dispatch =
+        let mut skip_array_during_method_dispatch =
             attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
+        let mut skip_boxed_slice_during_method_dispatch = false;
+        for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() {
+            for tt in tt.token_trees.iter() {
+                if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt {
+                    skip_array_during_method_dispatch |= ident.sym == sym::array;
+                    skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
+                }
+            }
+        }
         let rustc_has_incoherent_inherent_impls =
             attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists();
         let fundamental = attrs.by_key(&sym::fundamental).exists();
@@ -269,6 +279,7 @@ impl TraitData {
                 is_unsafe,
                 visibility,
                 skip_array_during_method_dispatch,
+                skip_boxed_slice_during_method_dispatch,
                 rustc_has_incoherent_inherent_impls,
                 fundamental,
             }),
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 7bf8af4cab..ff62344012 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -5,10 +5,11 @@ use std::fmt;
 use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
 use intern::sym;
 use once_cell::unsync::Lazy;
+use rustc_index::IndexVec;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
     usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
-    Captures, IndexVec, PatCx, PrivateUninhabitedField,
+    Captures, PatCx, PrivateUninhabitedField,
 };
 use smallvec::{smallvec, SmallVec};
 use stdx::never;
@@ -53,7 +54,7 @@ impl EnumVariantContiguousIndex {
     }
 }
 
-impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex {
+impl rustc_index::Idx for EnumVariantContiguousIndex {
     fn new(idx: usize) -> Self {
         EnumVariantContiguousIndex(idx)
     }
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index d3ddc883a8..c07bd08be4 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -5,7 +5,7 @@
 use std::ops::ControlFlow;
 
 use base_db::CrateId;
-use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
+use chalk_ir::{cast::Cast, UniverseIndex, WithKind};
 use hir_def::{
     data::{adt::StructFlags, ImplData},
     nameres::DefMap,
@@ -16,7 +16,6 @@ use hir_expand::name::Name;
 use intern::sym;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
-use span::Edition;
 use stdx::never;
 use triomphe::Arc;
 
@@ -25,12 +24,14 @@ use crate::{
     db::HirDatabase,
     error_lifetime, from_chalk_trait_id, from_foreign_def_id,
     infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
+    lang_items::is_box,
     primitive::{FloatTy, IntTy, UintTy},
     to_chalk_trait_id,
     utils::all_super_traits,
-    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance,
-    InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef,
-    TraitRefExt, Ty, TyBuilder, TyExt,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData,
+    Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution,
+    TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+    WhereClause,
 };
 
 /// This is used as a key for indexing impls.
@@ -1146,17 +1147,30 @@ fn iterate_trait_method_candidates(
     'traits: for &t in traits_in_scope {
         let data = db.trait_data(t);
 
-        // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
+        // Traits annotated with `#[rustc_skip_during_method_dispatch]` are skipped during
         // method resolution, if the receiver is an array, and we're compiling for editions before
         // 2021.
         // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
         // arrays.
         if data.skip_array_during_method_dispatch
-            && matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..))
+            && matches!(self_ty.kind(Interner), TyKind::Array(..))
         {
             // FIXME: this should really be using the edition of the method name's span, in case it
             // comes from a macro
-            if db.crate_graph()[krate].edition < Edition::Edition2021 {
+            if !db.crate_graph()[krate].edition.at_least_2021() {
+                continue;
+            }
+        }
+        if data.skip_boxed_slice_during_method_dispatch
+            && matches!(
+                self_ty.kind(Interner), TyKind::Adt(AdtId(def), subst)
+                if is_box(table.db, *def)
+                    && matches!(subst.at(Interner, 0).assert_ty_ref(Interner).kind(Interner), TyKind::Slice(..))
+            )
+        {
+            // FIXME: this should really be using the edition of the method name's span, in case it
+            // comes from a macro
+            if !db.crate_graph()[krate].edition.at_least_2024() {
                 continue;
             }
         }
@@ -1619,15 +1633,11 @@ fn generic_implements_goal(
     let kinds =
         binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| {
             let vk = match it.data(Interner) {
-                chalk_ir::GenericArgData::Ty(_) => {
-                    chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
-                }
-                chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
-                chalk_ir::GenericArgData::Const(c) => {
-                    chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
-                }
+                GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General),
+                GenericArgData::Lifetime(_) => VariableKind::Lifetime,
+                GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()),
             };
-            chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
+            WithKind::new(vk, UniverseIndex::ROOT)
         }));
     let binders = CanonicalVarKinds::from_iter(Interner, kinds);
 
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 1454d825b7..d810fac36a 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -400,6 +400,7 @@ define_symbols! {
     rustc_reservation_impl,
     rustc_safe_intrinsic,
     rustc_skip_array_during_method_dispatch,
+    rustc_skip_during_method_dispatch,
     semitransparent,
     shl_assign,
     shl,
@@ -455,4 +456,6 @@ define_symbols! {
     vectorcall,
     wasm,
     win64,
+    array,
+    boxed_slice,
 }
diff --git a/crates/parser/src/edition.rs b/crates/parser/src/edition.rs
index 831a482059..5d3d3cbd47 100644
--- a/crates/parser/src/edition.rs
+++ b/crates/parser/src/edition.rs
@@ -20,6 +20,10 @@ impl Edition {
         self >= Edition::Edition2024
     }
 
+    pub fn at_least_2021(self) -> bool {
+        self >= Edition::Edition2021
+    }
+
     pub fn at_least_2018(self) -> bool {
         self >= Edition::Edition2018
     }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cd8e7e339b..4c46fad4e5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -108,7 +108,7 @@ config_data! {
         /// targets and features, with the following base command line:
         ///
         /// ```bash
-        /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+        /// cargo check --quiet --workspace --message-format=json --all-targets
         /// ```
         /// .
         cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
diff --git a/crates/rust-analyzer/src/version.rs b/crates/rust-analyzer/src/version.rs
index 2ff967416c..1e829299e6 100644
--- a/crates/rust-analyzer/src/version.rs
+++ b/crates/rust-analyzer/src/version.rs
@@ -15,7 +15,7 @@ pub struct VersionInfo {
     pub version: &'static str,
     /// The release channel we were built for (stable/beta/nightly/dev).
     ///
-    /// `None` if not built via bootstrap.
+    /// `None` if not built via rustbuild.
     pub release_channel: Option<&'static str>,
     /// Information about the Git repository we may have been built from.
     ///
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 6fd81d0528..bbfae1839b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -82,7 +82,7 @@ By default, a cargo invocation will be constructed for the configured
 targets and features, with the following base command line:
 
 ```bash
-cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+cargo check --quiet --workspace --message-format=json --all-targets
 ```
 .
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 941b5ff4eb..9ddfcaa708 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -718,7 +718,7 @@
                 "title": "cargo",
                 "properties": {
                     "rust-analyzer.cargo.buildScripts.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.",
                         "default": null,
                         "type": [
                             "null",
diff --git a/rust-version b/rust-version
index 18e32e5c65..424c93a752 100644
--- a/rust-version
+++ b/rust-version
@@ -1,1 +1,1 @@
-a91f7d72f12efcc00ecf71591f066c534d45ddf7
+bcf1f6db4594ae6132378b179a30cdb3599a863d


Test patch:
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 63a83d403f..360a129283 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1640,6 +1640,55 @@ impl<'a, T> IntoIterator for &'a [T] {
     );
 }
 
+#[test]
+fn skip_during_method_dispatch() {
+    check_types(
+        r#"
+//- /main2018.rs crate:main2018 deps:core edition:2018
+use core::IntoIterator;
+
+fn f() {
+    let v = [4].into_iter();
+    v;
+  //^ &'? i32
+
+    let a = [0, 1].into_iter();
+    a;
+  //^ &'? i32
+}
+
+//- /main2021.rs crate:main2021 deps:core edition:2021
+use core::IntoIterator;
+
+fn f() {
+    let v = [4].into_iter();
+    v;
+  //^ i32
+
+    let a = [0, 1].into_iter();
+    a;
+  //^ &'? i32
+}
+
+//- /core.rs crate:core
+#[rustc_skip_during_method_dispatch(array, boxed_slice)]
+pub trait IntoIterator {
+    type Out;
+    fn into_iter(self) -> Self::Out;
+}
+
+impl<T> IntoIterator for [T; 1] {
+    type Out = T;
+    fn into_iter(self) -> Self::Out { loop {} }
+}
+impl<'a, T> IntoIterator for &'a [T] {
+    type Out = &'a T;
+    fn into_iter(self) -> Self::Out { loop {} }
+}
+    "#,
+    );
+}
+
 #[test]
 fn sized_blanket_impl() {
     check_infer(