Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 989000b

Browse files
committed
Auto merge of rust-lang#15893 - Veykril:unsafe-impls-diagnostic, r=Veykril
Diagnose incorrect unsafety for trait impls
2 parents 6e4538a + e21d21a commit 989000b

File tree

8 files changed

+204
-24
lines changed

8 files changed

+204
-24
lines changed

crates/hir-def/src/data.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ pub struct ImplData {
327327
pub self_ty: Interned<TypeRef>,
328328
pub items: Vec<AssocItemId>,
329329
pub is_negative: bool,
330+
pub is_unsafe: bool,
330331
// box it as the vec is usually empty anyways
331332
pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
332333
}
@@ -348,6 +349,7 @@ impl ImplData {
348349
let target_trait = impl_def.target_trait.clone();
349350
let self_ty = impl_def.self_ty.clone();
350351
let is_negative = impl_def.is_negative;
352+
let is_unsafe = impl_def.is_unsafe;
351353

352354
let mut collector =
353355
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
@@ -357,7 +359,14 @@ impl ImplData {
357359
let items = items.into_iter().map(|(_, item)| item).collect();
358360

359361
(
360-
Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }),
362+
Arc::new(ImplData {
363+
target_trait,
364+
self_ty,
365+
items,
366+
is_negative,
367+
is_unsafe,
368+
attribute_calls,
369+
}),
361370
diagnostics.into(),
362371
)
363372
}

crates/hir-def/src/item_tree.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ pub struct Impl {
709709
pub target_trait: Option<Interned<TraitRef>>,
710710
pub self_ty: Interned<TypeRef>,
711711
pub is_negative: bool,
712+
pub is_unsafe: bool,
712713
pub items: Box<[AssocItem]>,
713714
pub ast_id: FileAstId<ast::Impl>,
714715
}

crates/hir-def/src/item_tree/lower.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ impl<'a> Ctx<'a> {
492492
let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
493493
let self_ty = self.lower_type_ref(&impl_def.self_ty()?);
494494
let is_negative = impl_def.excl_token().is_some();
495+
let is_unsafe = impl_def.unsafe_token().is_some();
495496

496497
// We cannot use `assoc_items()` here as that does not include macro calls.
497498
let items = impl_def
@@ -506,7 +507,8 @@ impl<'a> Ctx<'a> {
506507
})
507508
.collect();
508509
let ast_id = self.source_ast_id_map.ast_id(impl_def);
509-
let res = Impl { generic_params, target_trait, self_ty, is_negative, items, ast_id };
510+
let res =
511+
Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id };
510512
Some(id(self.data().impls.alloc(res)))
511513
}
512514

crates/hir-def/src/item_tree/pretty.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,18 @@ impl Printer<'_> {
388388
wln!(self);
389389
}
390390
ModItem::Impl(it) => {
391-
let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } =
392-
&self.tree[it];
391+
let Impl {
392+
target_trait,
393+
self_ty,
394+
is_negative,
395+
is_unsafe,
396+
items,
397+
generic_params,
398+
ast_id: _,
399+
} = &self.tree[it];
400+
if *is_unsafe {
401+
w!(self, "unsafe");
402+
}
393403
w!(self, "impl");
394404
self.print_generic_params(generic_params);
395405
w!(self, " ");

crates/hir/src/diagnostics.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ diagnostics![
3838
IncorrectCase,
3939
InvalidDeriveTarget,
4040
IncoherentImpl,
41-
TraitImplOrphan,
4241
MacroDefError,
4342
MacroError,
4443
MacroExpansionParseError,
@@ -54,6 +53,8 @@ diagnostics![
5453
PrivateAssocItem,
5554
PrivateField,
5655
ReplaceFilterMapNextWithFindMap,
56+
TraitImplIncorrectSafety,
57+
TraitImplOrphan,
5758
TypedHole,
5859
TypeMismatch,
5960
UndeclaredLabel,
@@ -293,3 +294,11 @@ pub struct TraitImplOrphan {
293294
pub file_id: HirFileId,
294295
pub impl_: AstPtr<ast::Impl>,
295296
}
297+
298+
// FIXME: Split this off into the corresponding 4 rustc errors
299+
#[derive(Debug, PartialEq, Eq)]
300+
pub struct TraitImplIncorrectSafety {
301+
pub file_id: HirFileId,
302+
pub impl_: AstPtr<ast::Impl>,
303+
pub should_be_safe: bool,
304+
}

crates/hir/src/lib.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ use hir_def::{
5353
resolver::{HasResolver, Resolver},
5454
src::HasSource as _,
5555
AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
56-
EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId,
57-
InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup,
58-
MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
59-
TypeOrConstParamId, TypeParamId, UnionId,
56+
EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule,
57+
ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId,
58+
Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId,
59+
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
6060
};
6161
use hir_expand::{name::name, MacroCallKind};
6262
use hir_ty::{
@@ -89,17 +89,7 @@ use crate::db::{DefDatabase, HirDatabase};
8989

9090
pub use crate::{
9191
attrs::{resolve_doc_path_on, HasAttrs},
92-
diagnostics::{
93-
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
94-
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
95-
MacroExpansionParseError, MalformedDerive, MismatchedArgCount,
96-
MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
97-
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
98-
ReplaceFilterMapNextWithFindMap, TraitImplOrphan, TypeMismatch, TypedHole, UndeclaredLabel,
99-
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
100-
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
101-
UnresolvedProcMacro, UnusedMut, UnusedVariable,
102-
},
92+
diagnostics::*,
10393
has_source::HasSource,
10494
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
10595
};
@@ -613,22 +603,64 @@ impl Module {
613603
// FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow
614604
continue;
615605
}
606+
let ast_id_map = db.ast_id_map(file_id);
616607

617608
for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
618609
emit_def_diagnostic(db, acc, diag);
619610
}
620611

621612
if inherent_impls.invalid_impls().contains(&impl_def.id) {
622-
let ast_id_map = db.ast_id_map(file_id);
623-
624613
acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into())
625614
}
626615

627616
if !impl_def.check_orphan_rules(db) {
628-
let ast_id_map = db.ast_id_map(file_id);
629617
acc.push(TraitImplOrphan { impl_: ast_id_map.get(node.ast_id()), file_id }.into())
630618
}
631619

620+
let trait_ = impl_def.trait_(db);
621+
let trait_is_unsafe = trait_.map_or(false, |t| t.is_unsafe(db));
622+
let impl_is_negative = impl_def.is_negative(db);
623+
let impl_is_unsafe = impl_def.is_unsafe(db);
624+
625+
let drop_maybe_dangle = (|| {
626+
// FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper
627+
let trait_ = trait_?;
628+
let drop_trait = db.lang_item(self.krate().into(), LangItem::Drop)?.as_trait()?;
629+
if drop_trait != trait_.into() {
630+
return None;
631+
}
632+
let parent = impl_def.id.into();
633+
let generic_params = db.generic_params(parent);
634+
let lifetime_params = generic_params.lifetimes.iter().map(|(local_id, _)| {
635+
GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id })
636+
});
637+
let type_params = generic_params
638+
.iter()
639+
.filter(|(_, it)| it.type_param().is_some())
640+
.map(|(local_id, _)| {
641+
GenericParamId::TypeParamId(TypeParamId::from_unchecked(
642+
TypeOrConstParamId { parent, local_id },
643+
))
644+
});
645+
let res = type_params
646+
.chain(lifetime_params)
647+
.any(|p| db.attrs(AttrDefId::GenericParamId(p)).by_key("may_dangle").exists());
648+
Some(res)
649+
})()
650+
.unwrap_or(false);
651+
652+
match (impl_is_unsafe, trait_is_unsafe, impl_is_negative, drop_maybe_dangle) {
653+
// unsafe negative impl
654+
(true, _, true, _) |
655+
// unsafe impl for safe trait
656+
(true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: true }.into()),
657+
// safe impl for unsafe trait
658+
(false, true, false, _) |
659+
// safe impl of dangling drop
660+
(false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: false }.into()),
661+
_ => (),
662+
};
663+
632664
for item in impl_def.items(db) {
633665
let def: DefWithBody = match item {
634666
AssocItem::Function(it) => it.into(),
@@ -3404,7 +3436,7 @@ impl Impl {
34043436
}
34053437

34063438
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
3407-
db.impl_data(self.id).is_unique()
3439+
db.impl_data(self.id).is_unsafe
34083440
}
34093441

34103442
pub fn module(self, db: &dyn HirDatabase) -> Module {
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use hir::InFile;
2+
3+
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
4+
5+
// Diagnostic: trait-impl-incorrect-safety
6+
//
7+
// Diagnoses incorrect safety annotations of trait impls.
8+
pub(crate) fn trait_impl_incorrect_safety(
9+
ctx: &DiagnosticsContext<'_>,
10+
d: &hir::TraitImplIncorrectSafety,
11+
) -> Diagnostic {
12+
Diagnostic::new_with_syntax_node_ptr(
13+
ctx,
14+
DiagnosticCode::Ra("trait-impl-incorrect-safety", Severity::Error),
15+
if d.should_be_safe {
16+
"unsafe impl for safe trait"
17+
} else {
18+
"impl for unsafe trait needs to be unsafe"
19+
},
20+
InFile::new(d.file_id, d.impl_.clone().into()),
21+
)
22+
}
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use crate::tests::check_diagnostics;
27+
28+
#[test]
29+
fn simple() {
30+
check_diagnostics(
31+
r#"
32+
trait Safe {}
33+
unsafe trait Unsafe {}
34+
35+
impl Safe for () {}
36+
37+
impl Unsafe for () {}
38+
//^^^^^^^^^^^^^^^^^^^^^ error: impl for unsafe trait needs to be unsafe
39+
40+
unsafe impl Safe for () {}
41+
//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
42+
43+
unsafe impl Unsafe for () {}
44+
"#,
45+
);
46+
}
47+
48+
#[test]
49+
fn drop_may_dangle() {
50+
check_diagnostics(
51+
r#"
52+
#[lang = "drop"]
53+
trait Drop {}
54+
struct S<T>;
55+
struct L<'l>;
56+
57+
impl<T> Drop for S<T> {}
58+
59+
impl<#[may_dangle] T> Drop for S<T> {}
60+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: impl for unsafe trait needs to be unsafe
61+
62+
unsafe impl<T> Drop for S<T> {}
63+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
64+
65+
unsafe impl<#[may_dangle] T> Drop for S<T> {}
66+
67+
impl<'l> Drop for L<'l> {}
68+
69+
impl<#[may_dangle] 'l> Drop for L<'l> {}
70+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: impl for unsafe trait needs to be unsafe
71+
72+
unsafe impl<'l> Drop for L<'l> {}
73+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
74+
75+
unsafe impl<#[may_dangle] 'l> Drop for L<'l> {}
76+
"#,
77+
);
78+
}
79+
80+
#[test]
81+
fn negative() {
82+
check_diagnostics(
83+
r#"
84+
trait Trait {}
85+
86+
impl !Trait for () {}
87+
88+
unsafe impl !Trait for () {}
89+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
90+
91+
unsafe trait UnsafeTrait {}
92+
93+
impl !UnsafeTrait for () {}
94+
95+
unsafe impl !UnsafeTrait for () {}
96+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
97+
98+
"#,
99+
);
100+
}
101+
102+
#[test]
103+
fn inherent() {
104+
check_diagnostics(
105+
r#"
106+
struct S;
107+
108+
impl S {}
109+
110+
unsafe impl S {}
111+
//^^^^^^^^^^^^^^^^ error: unsafe impl for safe trait
112+
"#,
113+
);
114+
}
115+
}

crates/ide-diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod handlers {
4545
pub(crate) mod private_field;
4646
pub(crate) mod replace_filter_map_next_with_find_map;
4747
pub(crate) mod trait_impl_orphan;
48+
pub(crate) mod trait_impl_incorrect_safety;
4849
pub(crate) mod typed_hole;
4950
pub(crate) mod type_mismatch;
5051
pub(crate) mod unimplemented_builtin_macro;
@@ -360,6 +361,7 @@ pub fn diagnostics(
360361
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
361362
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
362363
AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d),
364+
AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d),
363365
AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
364366
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
365367
AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),

0 commit comments

Comments
 (0)