Skip to content

Commit 2bbb619

Browse files
committed
Auto merge of #114417 - chinedufn:fix-expect-unused-in-impl-block-rust-issue-114416, r=cjgillot
Fix multiple `expect` attribs in impl block Closes #114416
2 parents eea2614 + bf4df06 commit 2bbb619

File tree

2 files changed

+69
-34
lines changed

2 files changed

+69
-34
lines changed

compiler/rustc_passes/src/dead.rs

+39-34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// This implements the dead-code warning pass. It follows middle::reachable
2-
// closely. The idea is that all reachable symbols are live, codes called
3-
// from live codes are live, and everything else is dead.
1+
// This implements the dead-code warning pass.
2+
// All reachable symbols are live, code called from live code is live, code with certain lint
3+
// expectations such as `#[expect(unused)]` and `#[expect(dead_code)]` is live, and everything else
4+
// is dead.
45

56
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
67
use itertools::Itertools;
@@ -747,7 +748,7 @@ fn live_symbols_and_ignored_derived_traits(
747748
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
748749
}
749750

750-
struct DeadVariant {
751+
struct DeadItem {
751752
def_id: LocalDefId,
752753
name: Symbol,
753754
level: lint::Level,
@@ -785,7 +786,13 @@ impl<'tcx> DeadVisitor<'tcx> {
785786
ShouldWarnAboutField::Yes(is_positional)
786787
}
787788

788-
fn warn_multiple_dead_codes(
789+
// # Panics
790+
// All `dead_codes` must have the same lint level, otherwise we will intentionally ICE.
791+
// This is because we emit a multi-spanned lint using the lint level of the `dead_codes`'s
792+
// first local def id.
793+
// Prefer calling `Self.warn_dead_code` or `Self.warn_dead_code_grouped_by_lint_level`
794+
// since those methods group by lint level before calling this method.
795+
fn lint_at_single_level(
789796
&self,
790797
dead_codes: &[LocalDefId],
791798
participle: &str,
@@ -796,6 +803,15 @@ impl<'tcx> DeadVisitor<'tcx> {
796803
return;
797804
};
798805
let tcx = self.tcx;
806+
807+
let first_hir_id = tcx.hir().local_def_id_to_hir_id(first_id);
808+
let first_lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, first_hir_id).0;
809+
assert!(dead_codes.iter().skip(1).all(|id| {
810+
let hir_id = tcx.hir().local_def_id_to_hir_id(*id);
811+
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
812+
level == first_lint_level
813+
}));
814+
799815
let names: Vec<_> =
800816
dead_codes.iter().map(|&def_id| tcx.item_name(def_id.to_def_id())).collect();
801817
let spans: Vec<_> = dead_codes
@@ -876,31 +892,26 @@ impl<'tcx> DeadVisitor<'tcx> {
876892
}
877893
};
878894

879-
self.tcx.emit_spanned_lint(
880-
lint,
881-
tcx.hir().local_def_id_to_hir_id(first_id),
882-
MultiSpan::from_spans(spans),
883-
diag,
884-
);
895+
self.tcx.emit_spanned_lint(lint, first_hir_id, MultiSpan::from_spans(spans), diag);
885896
}
886897

887-
fn warn_dead_fields_and_variants(
898+
fn warn_multiple(
888899
&self,
889900
def_id: LocalDefId,
890901
participle: &str,
891-
dead_codes: Vec<DeadVariant>,
902+
dead_codes: Vec<DeadItem>,
892903
is_positional: bool,
893904
) {
894905
let mut dead_codes = dead_codes
895906
.iter()
896907
.filter(|v| !v.name.as_str().starts_with('_'))
897-
.collect::<Vec<&DeadVariant>>();
908+
.collect::<Vec<&DeadItem>>();
898909
if dead_codes.is_empty() {
899910
return;
900911
}
901912
dead_codes.sort_by_key(|v| v.level);
902913
for (_, group) in &dead_codes.into_iter().group_by(|v| v.level) {
903-
self.warn_multiple_dead_codes(
914+
self.lint_at_single_level(
904915
&group.map(|v| v.def_id).collect::<Vec<_>>(),
905916
participle,
906917
Some(def_id),
@@ -910,7 +921,7 @@ impl<'tcx> DeadVisitor<'tcx> {
910921
}
911922

912923
fn warn_dead_code(&mut self, id: LocalDefId, participle: &str) {
913-
self.warn_multiple_dead_codes(&[id], participle, None, false);
924+
self.lint_at_single_level(&[id], participle, None, false);
914925
}
915926

916927
fn check_definition(&mut self, def_id: LocalDefId) {
@@ -954,17 +965,16 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
954965
if let hir::ItemKind::Impl(impl_item) = tcx.hir().item(item).kind {
955966
let mut dead_items = Vec::new();
956967
for item in impl_item.items {
957-
let did = item.id.owner_id.def_id;
958-
if !visitor.is_live_code(did) {
959-
dead_items.push(did)
968+
let def_id = item.id.owner_id.def_id;
969+
if !visitor.is_live_code(def_id) {
970+
let name = tcx.item_name(def_id.to_def_id());
971+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
972+
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
973+
974+
dead_items.push(DeadItem { def_id, name, level })
960975
}
961976
}
962-
visitor.warn_multiple_dead_codes(
963-
&dead_items,
964-
"used",
965-
Some(item.owner_id.def_id),
966-
false,
967-
);
977+
visitor.warn_multiple(item.owner_id.def_id, "used", dead_items, false);
968978
}
969979

970980
if !live_symbols.contains(&item.owner_id.def_id) {
@@ -988,7 +998,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
988998
// Record to group diagnostics.
989999
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
9901000
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
991-
dead_variants.push(DeadVariant { def_id, name: variant.name, level });
1001+
dead_variants.push(DeadItem { def_id, name: variant.name, level });
9921002
continue;
9931003
}
9941004

@@ -1013,21 +1023,16 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10131023
hir_id,
10141024
)
10151025
.0;
1016-
Some(DeadVariant { def_id, name: field.name, level })
1026+
Some(DeadItem { def_id, name: field.name, level })
10171027
} else {
10181028
None
10191029
}
10201030
})
10211031
.collect();
1022-
visitor.warn_dead_fields_and_variants(def_id, "read", dead_fields, is_positional)
1032+
visitor.warn_multiple(def_id, "read", dead_fields, is_positional);
10231033
}
10241034

1025-
visitor.warn_dead_fields_and_variants(
1026-
item.owner_id.def_id,
1027-
"constructed",
1028-
dead_variants,
1029-
false,
1030-
);
1035+
visitor.warn_multiple(item.owner_id.def_id, "constructed", dead_variants, false);
10311036
}
10321037
}
10331038

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// check-pass
2+
// incremental
3+
4+
#![feature(lint_reasons)]
5+
#![warn(unused)]
6+
7+
struct OneUnused;
8+
struct TwoUnused;
9+
10+
impl OneUnused {
11+
#[expect(unused)]
12+
fn unused() {}
13+
}
14+
15+
impl TwoUnused {
16+
#[expect(unused)]
17+
fn unused1(){}
18+
19+
// This unused method has `#[expect(unused)]`, so the compiler should not emit a warning.
20+
// This ui test was added after a regression in the compiler where it did not recognize multiple
21+
// `#[expect(unused)]` annotations inside of impl blocks.
22+
// issue 114416
23+
#[expect(unused)]
24+
fn unused2(){}
25+
}
26+
27+
fn main() {
28+
let _ = OneUnused;
29+
let _ = TwoUnused;
30+
}

0 commit comments

Comments
 (0)