9104: Implement `#[rustc_skip_array_during_method_dispatch]` r=flodiebold a=jonas-schievink

haxx run the world

Closes https://github.com/rust-analyzer/rust-analyzer/issues/8552
Part of https://github.com/rust-analyzer/rust-analyzer/issues/9056

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-06-03 11:52:15 +00:00 committed by GitHub
commit c7eb19ebf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 2 deletions

View file

@ -143,6 +143,10 @@ pub struct TraitData {
pub is_auto: bool,
pub is_unsafe: bool,
pub visibility: RawVisibility,
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
/// method calls to this trait's methods when the receiver is an array and the crate edition is
/// 2015 or 2018.
pub skip_array_during_method_dispatch: bool,
}
impl TraitData {
@ -157,6 +161,10 @@ impl TraitData {
let container = AssocContainerId::TraitId(tr);
let visibility = item_tree[tr_def.visibility].clone();
let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
let skip_array_during_method_dispatch = item_tree
.attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
.by_key("rustc_skip_array_during_method_dispatch")
.exists();
let items = collect_items(
db,
@ -168,7 +176,14 @@ impl TraitData {
100,
);
Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility })
Arc::new(TraitData {
name,
items,
is_auto,
is_unsafe,
visibility,
skip_array_during_method_dispatch,
})
}
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {

View file

@ -5,7 +5,7 @@
use std::{iter, sync::Arc};
use arrayvec::ArrayVec;
use base_db::CrateId;
use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
use hir_def::{
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
@ -639,6 +639,7 @@ fn iterate_trait_method_candidates(
receiver_ty: Option<&Canonical<Ty>>,
callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
) -> bool {
let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
// if ty is `dyn Trait`, the trait doesn't need to be in scope
let inherent_trait =
self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@ -655,6 +656,19 @@ fn iterate_trait_method_candidates(
'traits: for t in traits {
let data = db.trait_data(t);
// Traits annotated with `#[rustc_skip_array_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 && receiver_is_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 {
continue;
}
}
// we'll be lazy about checking whether the type implements the
// trait, but if we find out it doesn't, we'll skip the rest of the
// iteration

View file

@ -1349,3 +1349,52 @@ fn f() {
"#,
);
}
#[test]
fn skip_array_during_method_dispatch() {
check_types(
r#"
//- /main2018.rs crate:main2018 deps:core
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_array_during_method_dispatch]
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 {}
}
impl<'a, T> IntoIterator for &'a [T] {
type Out = &'a T;
fn into_iter(self) -> Self::Out {}
}
"#,
);
}