Skip to content

Commit df377ca

Browse files
committed
Impl reachable_patterns lint in usefulness checking
Some usefulness code altered
1 parent 8073376 commit df377ca

File tree

8 files changed

+300
-113
lines changed

8 files changed

+300
-113
lines changed

compiler/rustc_lint/src/reachable_patterns.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for ReachablePattern {
5050
MatchSource::Normal,
5151
) = &expr.kind
5252
{
53-
if !arms.is_empty() {
54-
// TODO: can this ever not be there?
53+
if !arms.is_empty() && false {
5554
if let Some(ty::Adt(def, _)) =
5655
cx.maybe_typeck_results().map(|r| r.expr_ty(e).kind())
5756
{
@@ -148,7 +147,7 @@ fn has_reachable_attr(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
148147
&& attr.meta().map_or(false, |a| {
149148
a.meta_item_list()
150149
.and_then(|s| s.first().cloned())
151-
.map_or(false, |a| a.has_name(sym::reachable))
150+
.map_or(false, |a| a.has_name(sym::reachable_patterns))
152151
})
153152
})
154153
}

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::usefulness::{
2-
compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt, Reachability,
3-
UsefulnessReport,
2+
compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt,
3+
NonExhaustiveReachable, Reachability, UsefulnessReport,
44
};
55
use super::{PatCtxt, PatternError};
66

@@ -14,8 +14,8 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1414
use rustc_hir::{HirId, Pat};
1515
use rustc_middle::thir::PatKind;
1616
use rustc_middle::ty::{self, Ty, TyCtxt};
17-
use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
1817
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
18+
use rustc_session::lint::{builtin::BINDINGS_WITH_VARIANT_NAME, Level};
1919
use rustc_session::parse::feature_err;
2020
use rustc_session::Session;
2121
use rustc_span::{sym, Span};
@@ -74,6 +74,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
7474
};
7575
self.check_irrefutable(&loc.pat, msg, sp);
7676
self.check_patterns(&loc.pat);
77+
check_for_reachable_patterns_in_non_exhaustive(self, &loc.pat, loc.hir_id);
7778
}
7879

7980
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@@ -121,7 +122,7 @@ impl PatCtxt<'_, '_> {
121122
}
122123

123124
impl<'tcx> MatchVisitor<'_, 'tcx> {
124-
fn check_patterns(&mut self, pat: &Pat<'_>) {
125+
fn check_patterns(&mut self, pat: &'tcx Pat<'_>) {
125126
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
126127
if !self.tcx.features().bindings_after_at {
127128
check_legality_of_bindings_in_at_patterns(self, pat);
@@ -183,9 +184,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
183184

184185
let arms: Vec<_> = arms
185186
.iter()
186-
.map(|hir::Arm { pat, guard, .. }| MatchArm {
187+
.map(|hir::Arm { pat, guard, hir_id, .. }| MatchArm {
187188
pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
188189
hir_id: pat.hir_id,
190+
arm_hir_id: Some(*hir_id),
189191
has_guard: guard.is_some(),
190192
})
191193
.collect();
@@ -206,16 +208,24 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
206208
// since an empty matrix can occur when there are arms, if those arms all have guards.
207209
let is_empty_match = arms.is_empty();
208210
let witnesses = report.non_exhaustiveness_witnesses;
211+
let reachable = report.reachable;
209212
if !witnesses.is_empty() {
210-
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
213+
if let NonExhaustiveReachable::Lint { wildcard_span: Some(sp), level: Some(level) } =
214+
reachable
215+
{
216+
non_exhaustive_reachable_match(&cx, scrut_ty, scrut.span, sp, witnesses, level);
217+
} else {
218+
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
219+
}
211220
}
212221
}
213222

214223
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
215224
let mut cx = self.new_cx(pat.hir_id);
216225

217226
let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
218-
let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
227+
let arms =
228+
vec![MatchArm { pat: pattern, hir_id: pat.hir_id, arm_hir_id: None, has_guard: false }];
219229
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
220230

221231
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
@@ -343,6 +353,37 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
343353
});
344354
}
345355

356+
fn check_for_reachable_patterns_in_non_exhaustive<'p, 'tcx>(
357+
cx: &MatchVisitor<'p, 'tcx>,
358+
pat: &'tcx Pat<'tcx>,
359+
attr_hir_id: HirId,
360+
) {
361+
if let hir::PatKind::Struct(_, _fields @ [_, ..], true) = &pat.kind {
362+
if let ty::Adt(def, _) = cx.typeck_results.pat_ty(pat).kind() {
363+
if let Some(var) = def.variants.iter().next() {
364+
if var.is_field_list_non_exhaustive() && !def.did.is_local() {
365+
let mut mcx = cx.new_cx(pat.hir_id);
366+
367+
let (pattern, pattern_ty) = cx.lower_pattern(&mut mcx, pat, &mut false);
368+
let arms = vec![MatchArm {
369+
pat: pattern,
370+
hir_id: pat.hir_id,
371+
arm_hir_id: Some(attr_hir_id),
372+
has_guard: false,
373+
}];
374+
let report = compute_match_usefulness(&mcx, &arms, pat.hir_id, pattern_ty);
375+
println!("{:?}", report);
376+
// let missing = get_missing_fields(fields, &var.fields);
377+
// if !missing.is_empty() {
378+
// let last = fields.last().unwrap().span;
379+
// lint(cx, pat.span, last, missing)
380+
// }
381+
}
382+
}
383+
}
384+
}
385+
}
386+
346387
/// Checks for common cases of "catchall" patterns that may not be intended as such.
347388
fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
348389
use PatKind::*;
@@ -400,7 +441,7 @@ fn check_if_let_guard<'p, 'tcx>(
400441
pat: &'p super::Pat<'tcx>,
401442
pat_id: HirId,
402443
) {
403-
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
444+
let arms = [MatchArm { pat, hir_id: pat_id, arm_hir_id: None, has_guard: false }];
404445
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
405446
report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar);
406447

@@ -502,6 +543,10 @@ fn non_exhaustive_match<'p, 'tcx>(
502543
_ => false,
503544
};
504545

546+
if is_variant_list_non_exhaustive {
547+
println!("{:?}", witnesses);
548+
}
549+
505550
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
506551
err.help(
507552
"ensure that all possible cases are being handled, \
@@ -538,6 +583,38 @@ fn non_exhaustive_match<'p, 'tcx>(
538583
err.emit();
539584
}
540585

586+
/// Report that a match of a non_exhaustive type marked with `reachable_patterns` is not
587+
/// exhaustive enough.
588+
fn non_exhaustive_reachable_match<'p, 'tcx>(
589+
cx: &MatchCheckCtxt<'p, 'tcx>,
590+
scrut_ty: Ty<'tcx>,
591+
sp: Span,
592+
_wild_sp: Span,
593+
witnesses: Vec<super::Pat<'tcx>>,
594+
_level: Level,
595+
) {
596+
let joined_patterns = joined_uncovered_patterns(&witnesses);
597+
598+
let mut warn = cx
599+
.tcx
600+
.sess
601+
.struct_span_err(sp, &format!("reachable-patterns: {} not covered", joined_patterns));
602+
warn.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
603+
604+
// IS THIS HELPFUL
605+
// adt_defined_here(cx, &mut warn, scrut_ty, &witnesses);
606+
607+
warn.help(
608+
"ensure that all possible cases are being handled by adding the suggested match arms",
609+
);
610+
warn.note(&format!(
611+
"the matched value is of type `{}` and the wildcard has the `reachable_patterns` attribute",
612+
scrut_ty,
613+
));
614+
615+
warn.emit();
616+
}
617+
541618
fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
542619
const LIMIT: usize = 3;
543620
match witnesses {

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,17 @@ pub(super) enum Constructor<'tcx> {
605605
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
606606
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
607607
NonExhaustive,
608+
// /// Used to distinguish between patterns of non_exhaustive and non_exhaustive with
609+
// /// the reachable_patterns lint enabled.
610+
// ///
611+
// /// ```rust
612+
// /// match NonExhaustiveEnum {
613+
// /// Variant1 => {},
614+
// /// #[warn(reachable_patterns)]
615+
// /// _ => {},
616+
// /// }
617+
// /// ```
618+
// NonExhaustiveReachable,
608619
/// Stands for constructors that are not seen in the matrix, as explained in the documentation
609620
/// for [`SplitWildcard`].
610621
Missing,
@@ -753,7 +764,8 @@ impl<'tcx> Constructor<'tcx> {
753764
pub(super) fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool {
754765
// This must be kept in sync with `is_covered_by_any`.
755766
match (self, other) {
756-
// Wildcards cover anything
767+
// We ignore the wildcard when this lint is enabled
768+
// (NonExhaustiveReachable, Wildcard) => false,
757769
(_, Wildcard) => true,
758770
// The missing ctors are not covered by anything in the matrix except wildcards.
759771
(Missing | Wildcard, _) => false,
@@ -829,6 +841,7 @@ impl<'tcx> Constructor<'tcx> {
829841
.any(|other| slice.is_covered_by(other)),
830842
// This constructor is never covered by anything else
831843
NonExhaustive => false,
844+
// NonExhaustiveReachable => false,
832845
Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => {
833846
span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
834847
}
@@ -862,6 +875,8 @@ pub(super) struct SplitWildcard<'tcx> {
862875
impl<'tcx> SplitWildcard<'tcx> {
863876
pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self {
864877
debug!("SplitWildcard::new({:?})", pcx.ty);
878+
println!("SplitWildcard::new({:?})", pcx.ty);
879+
865880
let cx = pcx.cx;
866881
let make_range = |start, end| {
867882
IntRange(
@@ -919,7 +934,9 @@ impl<'tcx> SplitWildcard<'tcx> {
919934
&& !cx.tcx.features().exhaustive_patterns
920935
&& !pcx.is_top_level;
921936

922-
if is_secretly_empty || is_declared_nonexhaustive {
937+
if is_declared_nonexhaustive && pcx.non_exhaustive_reachable {
938+
def.variants.indices().map(|idx| Variant(idx)).collect()
939+
} else if is_secretly_empty || is_declared_nonexhaustive {
923940
smallvec![NonExhaustive]
924941
} else if cx.tcx.features().exhaustive_patterns {
925942
// If `exhaustive_patterns` is enabled, we exclude variants known to be
@@ -975,6 +992,9 @@ impl<'tcx> SplitWildcard<'tcx> {
975992
// This type is one for which we cannot list constructors, like `str` or `f64`.
976993
_ => smallvec![NonExhaustive],
977994
};
995+
996+
println!("{:?}", all_ctors);
997+
978998
SplitWildcard { matrix_ctors: Vec::new(), all_ctors }
979999
}
9801000

@@ -1189,15 +1209,18 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11891209
/// This is roughly the inverse of `specialize_constructor`.
11901210
///
11911211
/// Examples:
1192-
/// `ctor`: `Constructor::Single`
1193-
/// `ty`: `Foo(u32, u32, u32)`
1194-
/// `self`: `[10, 20, _]`
1212+
///
1213+
/// ```text
1214+
/// ctor: `Constructor::Single`
1215+
/// ty: `Foo(u32, u32, u32)`
1216+
/// self: `[10, 20, _]`
11951217
/// returns `Foo(10, 20, _)`
11961218
///
1197-
/// `ctor`: `Constructor::Variant(Option::Some)`
1198-
/// `ty`: `Option<bool>`
1199-
/// `self`: `[false]`
1219+
/// ctor: `Constructor::Variant(Option::Some)`
1220+
/// ty: `Option<bool>`
1221+
/// self: `[false]`
12001222
/// returns `Some(false)`
1223+
/// ```
12011224
pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> {
12021225
let subpatterns_and_indices = self.patterns_and_indices();
12031226
let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned();
@@ -1263,6 +1286,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12631286
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
12641287
IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
12651288
NonExhaustive => PatKind::Wild,
1289+
// NonExhaustiveReachable => PatKind::Wild,
12661290
Wildcard => return Pat::wildcard_from_ty(pcx.ty),
12671291
Opaque => bug!("we should not try to apply an opaque constructor"),
12681292
Missing => bug!(

0 commit comments

Comments
 (0)