Skip to content

Commit f34b246

Browse files
committed
lint incoherent inherent impls
1 parent c15335c commit f34b246

File tree

12 files changed

+284
-51
lines changed

12 files changed

+284
-51
lines changed

crates/hir-def/src/data.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct FunctionData {
3535
pub visibility: RawVisibility,
3636
pub abi: Option<Interned<str>>,
3737
pub legacy_const_generics_indices: Box<[u32]>,
38+
pub rustc_allow_incoherent_impl: bool,
3839
flags: FnFlags,
3940
}
4041

@@ -84,13 +85,14 @@ impl FunctionData {
8485
}
8586
}
8687

87-
let legacy_const_generics_indices = item_tree
88-
.attrs(db, krate, ModItem::from(loc.id.value).into())
88+
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
89+
let legacy_const_generics_indices = attrs
8990
.by_key("rustc_legacy_const_generics")
9091
.tt_values()
9192
.next()
9293
.map(parse_rustc_legacy_const_generics)
9394
.unwrap_or_default();
95+
let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
9496

9597
Arc::new(FunctionData {
9698
name: func.name.clone(),
@@ -108,6 +110,7 @@ impl FunctionData {
108110
abi: func.abi.clone(),
109111
legacy_const_generics_indices,
110112
flags,
113+
rustc_allow_incoherent_impl,
111114
})
112115
}
113116

@@ -171,6 +174,7 @@ pub struct TypeAliasData {
171174
pub visibility: RawVisibility,
172175
pub is_extern: bool,
173176
pub rustc_has_incoherent_inherent_impls: bool,
177+
pub rustc_allow_incoherent_impl: bool,
174178
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
175179
pub bounds: Vec<Interned<TypeBound>>,
176180
}
@@ -189,17 +193,22 @@ impl TypeAliasData {
189193
item_tree[typ.visibility].clone()
190194
};
191195

192-
let rustc_has_incoherent_inherent_impls = item_tree
193-
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
194-
.by_key("rustc_has_incoherent_inherent_impls")
195-
.exists();
196+
let attrs = item_tree.attrs(
197+
db,
198+
loc.container.module(db).krate(),
199+
ModItem::from(loc.id.value).into(),
200+
);
201+
let rustc_has_incoherent_inherent_impls =
202+
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
203+
let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
196204

197205
Arc::new(TypeAliasData {
198206
name: typ.name.clone(),
199207
type_ref: typ.type_ref.clone(),
200208
visibility,
201209
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
202210
rustc_has_incoherent_inherent_impls,
211+
rustc_allow_incoherent_impl,
203212
bounds: typ.bounds.to_vec(),
204213
})
205214
}
@@ -450,6 +459,7 @@ pub struct ConstData {
450459
pub name: Option<Name>,
451460
pub type_ref: Interned<TypeRef>,
452461
pub visibility: RawVisibility,
462+
pub rustc_allow_incoherent_impl: bool,
453463
}
454464

455465
impl ConstData {
@@ -463,10 +473,16 @@ impl ConstData {
463473
item_tree[konst.visibility].clone()
464474
};
465475

476+
let rustc_allow_incoherent_impl = item_tree
477+
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
478+
.by_key("rustc_allow_incoherent_impl")
479+
.exists();
480+
466481
Arc::new(ConstData {
467482
name: konst.name.clone(),
468483
type_ref: konst.type_ref.clone(),
469484
visibility,
485+
rustc_allow_incoherent_impl,
470486
})
471487
}
472488
}

crates/hir-def/src/nameres.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub struct DefMap {
120120
registered_tools: Vec<SmolStr>,
121121
/// Unstable features of Rust enabled with `#![feature(A, B)]`.
122122
unstable_features: FxHashSet<SmolStr>,
123+
/// #[rustc_coherence_is_core]
124+
rustc_coherence_is_core: bool,
123125

124126
edition: Edition,
125127
recursion_limit: Option<u32>,
@@ -292,6 +294,7 @@ impl DefMap {
292294
registered_tools: Vec::new(),
293295
unstable_features: FxHashSet::default(),
294296
diagnostics: Vec::new(),
297+
rustc_coherence_is_core: false,
295298
}
296299
}
297300

@@ -325,6 +328,10 @@ impl DefMap {
325328
self.unstable_features.contains(feature)
326329
}
327330

331+
pub fn is_rustc_coherence_is_core(&self) -> bool {
332+
self.rustc_coherence_is_core
333+
}
334+
328335
pub fn root(&self) -> LocalModuleId {
329336
self.root
330337
}
@@ -337,7 +344,7 @@ impl DefMap {
337344
self.proc_macro_loading_error.as_deref()
338345
}
339346

340-
pub(crate) fn krate(&self) -> CrateId {
347+
pub fn krate(&self) -> CrateId {
341348
self.krate
342349
}
343350

@@ -502,6 +509,7 @@ impl DefMap {
502509
krate: _,
503510
prelude: _,
504511
root: _,
512+
rustc_coherence_is_core: _,
505513
} = self;
506514

507515
extern_prelude.shrink_to_fit();

crates/hir-def/src/nameres/collector.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ impl DefCollector<'_> {
296296
continue;
297297
}
298298

299+
if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
300+
self.def_map.rustc_coherence_is_core = true;
301+
continue;
302+
}
303+
299304
if *attr_name == hir_expand::name![feature] {
300305
let features =
301306
attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(

crates/hir-ty/src/diagnostics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ pub use crate::diagnostics::{
1111
},
1212
unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr},
1313
};
14+
15+
#[derive(Debug, PartialEq, Eq)]
16+
pub struct IncoherentImpl {
17+
pub file_id: hir_expand::HirFileId,
18+
pub impl_: syntax::AstPtr<syntax::ast::Impl>,
19+
}

crates/hir-ty/src/method_resolution.rs

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use stdx::never;
1919
use crate::{
2020
autoderef::{self, AutoderefKind},
2121
db::HirDatabase,
22-
from_foreign_def_id,
22+
from_chalk_trait_id, from_foreign_def_id,
2323
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
2424
primitive::{FloatTy, IntTy, UintTy},
2525
static_lifetime, to_chalk_trait_id,
@@ -266,11 +266,12 @@ impl TraitImpls {
266266
#[derive(Debug, Eq, PartialEq)]
267267
pub struct InherentImpls {
268268
map: FxHashMap<TyFingerprint, Vec<ImplId>>,
269+
invalid_impls: Vec<ImplId>,
269270
}
270271

271272
impl InherentImpls {
272273
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
273-
let mut impls = Self { map: FxHashMap::default() };
274+
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
274275

275276
let crate_def_map = db.crate_def_map(krate);
276277
impls.collect_def_map(db, &crate_def_map);
@@ -283,7 +284,7 @@ impl InherentImpls {
283284
db: &dyn HirDatabase,
284285
block: BlockId,
285286
) -> Option<Arc<Self>> {
286-
let mut impls = Self { map: FxHashMap::default() };
287+
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
287288
if let Some(block_def_map) = db.block_def_map(block) {
288289
impls.collect_def_map(db, &block_def_map);
289290
impls.shrink_to_fit();
@@ -306,11 +307,17 @@ impl InherentImpls {
306307
}
307308

308309
let self_ty = db.impl_self_ty(impl_id);
309-
let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
310-
if let Some(fp) = fp {
311-
self.map.entry(fp).or_default().push(impl_id);
310+
let self_ty = self_ty.skip_binders();
311+
312+
match is_inherent_impl_coherent(db, def_map, &data, self_ty) {
313+
true => {
314+
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
315+
if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) {
316+
self.map.entry(fp).or_default().push(impl_id);
317+
}
318+
}
319+
false => self.invalid_impls.push(impl_id),
312320
}
313-
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
314321
}
315322

316323
// To better support custom derives, collect impls in all unnamed const items.
@@ -334,6 +341,10 @@ impl InherentImpls {
334341
pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
335342
self.map.values().flat_map(|v| v.iter().copied())
336343
}
344+
345+
pub fn invalid_impls(&self) -> &[ImplId] {
346+
&self.invalid_impls
347+
}
337348
}
338349

339350
pub(crate) fn incoherent_inherent_impl_crates(
@@ -775,6 +786,90 @@ fn find_matching_impl(
775786
}
776787
}
777788

789+
fn is_inherent_impl_coherent(
790+
db: &dyn HirDatabase,
791+
def_map: &DefMap,
792+
impl_data: &ImplData,
793+
self_ty: &Ty,
794+
) -> bool {
795+
let self_ty = self_ty.kind(Interner);
796+
let impl_allowed = match self_ty {
797+
TyKind::Tuple(_, _)
798+
| TyKind::FnDef(_, _)
799+
| TyKind::Array(_, _)
800+
| TyKind::Never
801+
| TyKind::Raw(_, _)
802+
| TyKind::Ref(_, _, _)
803+
| TyKind::Slice(_)
804+
| TyKind::Str
805+
| TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(),
806+
807+
&TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(),
808+
// FIXME: Factor out the principal trait fetching into a function
809+
TyKind::Dyn(it) => it
810+
.bounds
811+
.skip_binders()
812+
.interned()
813+
.get(0)
814+
.and_then(|b| match b.skip_binders() {
815+
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
816+
_ => None,
817+
})
818+
.map_or(false, |trait_ref| {
819+
from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()
820+
== def_map.krate()
821+
}),
822+
823+
_ => true,
824+
};
825+
impl_allowed || {
826+
let rustc_has_incoherent_inherent_impls = match self_ty {
827+
TyKind::Tuple(_, _)
828+
| TyKind::FnDef(_, _)
829+
| TyKind::Array(_, _)
830+
| TyKind::Never
831+
| TyKind::Raw(_, _)
832+
| TyKind::Ref(_, _, _)
833+
| TyKind::Slice(_)
834+
| TyKind::Str
835+
| TyKind::Scalar(_) => true,
836+
837+
&TyKind::Adt(AdtId(adt), _) => match adt {
838+
hir_def::AdtId::StructId(it) => {
839+
db.struct_data(it).rustc_has_incoherent_inherent_impls
840+
}
841+
hir_def::AdtId::UnionId(it) => {
842+
db.union_data(it).rustc_has_incoherent_inherent_impls
843+
}
844+
hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
845+
},
846+
// FIXME: Factor out the principal trait fetching into a function
847+
TyKind::Dyn(it) => it
848+
.bounds
849+
.skip_binders()
850+
.interned()
851+
.get(0)
852+
.and_then(|b| match b.skip_binders() {
853+
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
854+
_ => None,
855+
})
856+
.map_or(false, |trait_ref| {
857+
db.trait_data(from_chalk_trait_id(trait_ref.trait_id))
858+
.rustc_has_incoherent_inherent_impls
859+
}),
860+
861+
_ => false,
862+
};
863+
rustc_has_incoherent_inherent_impls
864+
&& !impl_data.items.is_empty()
865+
&& impl_data.items.iter().copied().all(|assoc| match assoc {
866+
AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
867+
AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
868+
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
869+
})
870+
}
871+
}
872+
778873
pub fn iterate_path_candidates(
779874
ty: &Canonical<Ty>,
780875
db: &dyn HirDatabase,

crates/hir-ty/src/tests/method_resolution.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fn infer_slice_method() {
99
check_types(
1010
r#"
1111
impl<T> [T] {
12+
#[rustc_allow_incoherent_impl]
1213
fn foo(&self) -> T {
1314
loop {}
1415
}
@@ -35,6 +36,7 @@ fn test() {
3536
//- /lib.rs crate:other_crate
3637
mod foo {
3738
impl f32 {
39+
#[rustc_allow_incoherent_impl]
3840
pub fn foo(self) -> f32 { 0. }
3941
}
4042
}
@@ -47,6 +49,7 @@ fn infer_array_inherent_impl() {
4749
check_types(
4850
r#"
4951
impl<T, const N: usize> [T; N] {
52+
#[rustc_allow_incoherent_impl]
5053
fn foo(&self) -> T {
5154
loop {}
5255
}
@@ -1437,6 +1440,7 @@ fn resolve_const_generic_array_methods() {
14371440
r#"
14381441
#[lang = "array"]
14391442
impl<T, const N: usize> [T; N] {
1443+
#[rustc_allow_incoherent_impl]
14401444
pub fn map<F, U>(self, f: F) -> [U; N]
14411445
where
14421446
F: FnMut(T) -> U,
@@ -1445,6 +1449,7 @@ impl<T, const N: usize> [T; N] {
14451449
14461450
#[lang = "slice"]
14471451
impl<T> [T] {
1452+
#[rustc_allow_incoherent_impl]
14481453
pub fn map<F, U>(self, f: F) -> &[U]
14491454
where
14501455
F: FnMut(T) -> U,
@@ -1468,6 +1473,7 @@ struct Const<const N: usize>;
14681473
14691474
#[lang = "array"]
14701475
impl<T, const N: usize> [T; N] {
1476+
#[rustc_allow_incoherent_impl]
14711477
pub fn my_map<F, U, const X: usize>(self, f: F, c: Const<X>) -> [U; X]
14721478
where
14731479
F: FnMut(T) -> U,
@@ -1476,6 +1482,7 @@ impl<T, const N: usize> [T; N] {
14761482
14771483
#[lang = "slice"]
14781484
impl<T> [T] {
1485+
#[rustc_allow_incoherent_impl]
14791486
pub fn my_map<F, const X: usize, U>(self, f: F, c: Const<X>) -> &[U]
14801487
where
14811488
F: FnMut(T) -> U,
@@ -1874,14 +1881,14 @@ fn incoherent_impls() {
18741881
pub struct Box<T>(T);
18751882
use core::error::Error;
18761883
1877-
#[rustc_allow_incoherent_impl]
18781884
impl dyn Error {
1885+
#[rustc_allow_incoherent_impl]
18791886
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
18801887
loop {}
18811888
}
18821889
}
1883-
#[rustc_allow_incoherent_impl]
18841890
impl dyn Error + Send {
1891+
#[rustc_allow_incoherent_impl]
18851892
/// Attempts to downcast the box to a concrete type.
18861893
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
18871894
let err: Box<dyn Error> = self;

0 commit comments

Comments
 (0)