mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
lint incoherent inherent impls
This commit is contained in:
parent
c15335c8b0
commit
f34b2469bd
12 changed files with 284 additions and 51 deletions
|
@ -11,3 +11,9 @@ pub use crate::diagnostics::{
|
|||
},
|
||||
unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct IncoherentImpl {
|
||||
pub file_id: hir_expand::HirFileId,
|
||||
pub impl_: syntax::AstPtr<syntax::ast::Impl>,
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use stdx::never;
|
|||
use crate::{
|
||||
autoderef::{self, AutoderefKind},
|
||||
db::HirDatabase,
|
||||
from_foreign_def_id,
|
||||
from_chalk_trait_id, from_foreign_def_id,
|
||||
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
|
||||
primitive::{FloatTy, IntTy, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
|
@ -266,11 +266,12 @@ impl TraitImpls {
|
|||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct InherentImpls {
|
||||
map: FxHashMap<TyFingerprint, Vec<ImplId>>,
|
||||
invalid_impls: Vec<ImplId>,
|
||||
}
|
||||
|
||||
impl InherentImpls {
|
||||
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
|
||||
let mut impls = Self { map: FxHashMap::default() };
|
||||
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
|
||||
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
impls.collect_def_map(db, &crate_def_map);
|
||||
|
@ -283,7 +284,7 @@ impl InherentImpls {
|
|||
db: &dyn HirDatabase,
|
||||
block: BlockId,
|
||||
) -> Option<Arc<Self>> {
|
||||
let mut impls = Self { map: FxHashMap::default() };
|
||||
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
|
||||
if let Some(block_def_map) = db.block_def_map(block) {
|
||||
impls.collect_def_map(db, &block_def_map);
|
||||
impls.shrink_to_fit();
|
||||
|
@ -306,11 +307,17 @@ impl InherentImpls {
|
|||
}
|
||||
|
||||
let self_ty = db.impl_self_ty(impl_id);
|
||||
let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
|
||||
if let Some(fp) = fp {
|
||||
self.map.entry(fp).or_default().push(impl_id);
|
||||
let self_ty = self_ty.skip_binders();
|
||||
|
||||
match is_inherent_impl_coherent(db, def_map, &data, self_ty) {
|
||||
true => {
|
||||
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
|
||||
if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) {
|
||||
self.map.entry(fp).or_default().push(impl_id);
|
||||
}
|
||||
}
|
||||
false => self.invalid_impls.push(impl_id),
|
||||
}
|
||||
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
|
||||
}
|
||||
|
||||
// To better support custom derives, collect impls in all unnamed const items.
|
||||
|
@ -334,6 +341,10 @@ impl InherentImpls {
|
|||
pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
|
||||
self.map.values().flat_map(|v| v.iter().copied())
|
||||
}
|
||||
|
||||
pub fn invalid_impls(&self) -> &[ImplId] {
|
||||
&self.invalid_impls
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn incoherent_inherent_impl_crates(
|
||||
|
@ -775,6 +786,90 @@ fn find_matching_impl(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_inherent_impl_coherent(
|
||||
db: &dyn HirDatabase,
|
||||
def_map: &DefMap,
|
||||
impl_data: &ImplData,
|
||||
self_ty: &Ty,
|
||||
) -> bool {
|
||||
let self_ty = self_ty.kind(Interner);
|
||||
let impl_allowed = match self_ty {
|
||||
TyKind::Tuple(_, _)
|
||||
| TyKind::FnDef(_, _)
|
||||
| TyKind::Array(_, _)
|
||||
| TyKind::Never
|
||||
| TyKind::Raw(_, _)
|
||||
| TyKind::Ref(_, _, _)
|
||||
| TyKind::Slice(_)
|
||||
| TyKind::Str
|
||||
| TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(),
|
||||
|
||||
&TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(),
|
||||
// FIXME: Factor out the principal trait fetching into a function
|
||||
TyKind::Dyn(it) => it
|
||||
.bounds
|
||||
.skip_binders()
|
||||
.interned()
|
||||
.get(0)
|
||||
.and_then(|b| match b.skip_binders() {
|
||||
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
|
||||
_ => None,
|
||||
})
|
||||
.map_or(false, |trait_ref| {
|
||||
from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()
|
||||
== def_map.krate()
|
||||
}),
|
||||
|
||||
_ => true,
|
||||
};
|
||||
impl_allowed || {
|
||||
let rustc_has_incoherent_inherent_impls = match self_ty {
|
||||
TyKind::Tuple(_, _)
|
||||
| TyKind::FnDef(_, _)
|
||||
| TyKind::Array(_, _)
|
||||
| TyKind::Never
|
||||
| TyKind::Raw(_, _)
|
||||
| TyKind::Ref(_, _, _)
|
||||
| TyKind::Slice(_)
|
||||
| TyKind::Str
|
||||
| TyKind::Scalar(_) => true,
|
||||
|
||||
&TyKind::Adt(AdtId(adt), _) => match adt {
|
||||
hir_def::AdtId::StructId(it) => {
|
||||
db.struct_data(it).rustc_has_incoherent_inherent_impls
|
||||
}
|
||||
hir_def::AdtId::UnionId(it) => {
|
||||
db.union_data(it).rustc_has_incoherent_inherent_impls
|
||||
}
|
||||
hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
|
||||
},
|
||||
// FIXME: Factor out the principal trait fetching into a function
|
||||
TyKind::Dyn(it) => it
|
||||
.bounds
|
||||
.skip_binders()
|
||||
.interned()
|
||||
.get(0)
|
||||
.and_then(|b| match b.skip_binders() {
|
||||
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
|
||||
_ => None,
|
||||
})
|
||||
.map_or(false, |trait_ref| {
|
||||
db.trait_data(from_chalk_trait_id(trait_ref.trait_id))
|
||||
.rustc_has_incoherent_inherent_impls
|
||||
}),
|
||||
|
||||
_ => false,
|
||||
};
|
||||
rustc_has_incoherent_inherent_impls
|
||||
&& !impl_data.items.is_empty()
|
||||
&& impl_data.items.iter().copied().all(|assoc| match assoc {
|
||||
AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
|
||||
AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
|
||||
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate_path_candidates(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
|
|
|
@ -9,6 +9,7 @@ fn infer_slice_method() {
|
|||
check_types(
|
||||
r#"
|
||||
impl<T> [T] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
fn foo(&self) -> T {
|
||||
loop {}
|
||||
}
|
||||
|
@ -35,6 +36,7 @@ fn test() {
|
|||
//- /lib.rs crate:other_crate
|
||||
mod foo {
|
||||
impl f32 {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn foo(self) -> f32 { 0. }
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +49,7 @@ fn infer_array_inherent_impl() {
|
|||
check_types(
|
||||
r#"
|
||||
impl<T, const N: usize> [T; N] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
fn foo(&self) -> T {
|
||||
loop {}
|
||||
}
|
||||
|
@ -1437,6 +1440,7 @@ fn resolve_const_generic_array_methods() {
|
|||
r#"
|
||||
#[lang = "array"]
|
||||
impl<T, const N: usize> [T; N] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn map<F, U>(self, f: F) -> [U; N]
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
|
@ -1445,6 +1449,7 @@ impl<T, const N: usize> [T; N] {
|
|||
|
||||
#[lang = "slice"]
|
||||
impl<T> [T] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn map<F, U>(self, f: F) -> &[U]
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
|
@ -1468,6 +1473,7 @@ struct Const<const N: usize>;
|
|||
|
||||
#[lang = "array"]
|
||||
impl<T, const N: usize> [T; N] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn my_map<F, U, const X: usize>(self, f: F, c: Const<X>) -> [U; X]
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
|
@ -1476,6 +1482,7 @@ impl<T, const N: usize> [T; N] {
|
|||
|
||||
#[lang = "slice"]
|
||||
impl<T> [T] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn my_map<F, const X: usize, U>(self, f: F, c: Const<X>) -> &[U]
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
|
@ -1874,14 +1881,14 @@ fn incoherent_impls() {
|
|||
pub struct Box<T>(T);
|
||||
use core::error::Error;
|
||||
|
||||
#[rustc_allow_incoherent_impl]
|
||||
impl dyn Error {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
#[rustc_allow_incoherent_impl]
|
||||
impl dyn Error + Send {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
/// Attempts to downcast the box to a concrete type.
|
||||
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
|
||||
let err: Box<dyn Error> = self;
|
||||
|
|
|
@ -1116,21 +1116,22 @@ fn infer_inherent_method() {
|
|||
fn infer_inherent_method_str() {
|
||||
check_infer(
|
||||
r#"
|
||||
#[lang = "str"]
|
||||
impl str {
|
||||
fn foo(&self) -> i32 {}
|
||||
}
|
||||
#![rustc_coherence_is_core]
|
||||
#[lang = "str"]
|
||||
impl str {
|
||||
fn foo(&self) -> i32 {}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
"foo".foo();
|
||||
}
|
||||
"#,
|
||||
fn test() {
|
||||
"foo".foo();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
39..43 'self': &str
|
||||
52..54 '{}': i32
|
||||
68..88 '{ ...o(); }': ()
|
||||
74..79 '"foo"': &str
|
||||
74..85 '"foo".foo()': i32
|
||||
67..71 'self': &str
|
||||
80..82 '{}': i32
|
||||
96..116 '{ ...o(); }': ()
|
||||
102..107 '"foo"': &str
|
||||
102..113 '"foo".foo()': i32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -2640,6 +2641,7 @@ impl<T> [T] {}
|
|||
|
||||
#[lang = "slice_alloc"]
|
||||
impl<T> [T] {
|
||||
#[rustc_allow_incoherent_impl]
|
||||
pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -2655,22 +2657,22 @@ struct Astruct;
|
|||
impl B for Astruct {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
569..573 'self': Box<[T], A>
|
||||
602..634 '{ ... }': Vec<T, A>
|
||||
648..761 '{ ...t]); }': ()
|
||||
658..661 'vec': Vec<i32, Global>
|
||||
664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
|
||||
664..691 '<[_]>:...1i32])': Vec<i32, Global>
|
||||
680..690 'box [1i32]': Box<[i32; 1], Global>
|
||||
684..690 '[1i32]': [i32; 1]
|
||||
685..689 '1i32': i32
|
||||
701..702 'v': Vec<Box<dyn B, Global>, Global>
|
||||
722..739 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
|
||||
722..758 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
|
||||
740..757 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
|
||||
744..757 '[box Astruct]': [Box<dyn B, Global>; 1]
|
||||
745..756 'box Astruct': Box<Astruct, Global>
|
||||
749..756 'Astruct': Astruct
|
||||
604..608 'self': Box<[T], A>
|
||||
637..669 '{ ... }': Vec<T, A>
|
||||
683..796 '{ ...t]); }': ()
|
||||
693..696 'vec': Vec<i32, Global>
|
||||
699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
|
||||
699..726 '<[_]>:...1i32])': Vec<i32, Global>
|
||||
715..725 'box [1i32]': Box<[i32; 1], Global>
|
||||
719..725 '[1i32]': [i32; 1]
|
||||
720..724 '1i32': i32
|
||||
736..737 'v': Vec<Box<dyn B, Global>, Global>
|
||||
757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
|
||||
757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
|
||||
775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
|
||||
779..792 '[box Astruct]': [Box<dyn B, Global>; 1]
|
||||
780..791 'box Astruct': Box<Astruct, Global>
|
||||
784..791 'Astruct': Astruct
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue