]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/thir/pattern/check_match.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / thir / pattern / check_match.rs
CommitLineData
c295e0f8 1use super::deconstruct_pat::{Constructor, DeconstructedPat};
fc512014 2use super::usefulness::{
c295e0f8 3 compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
fc512014 4};
223e47cc 5
9c376795
FG
6use crate::errors::*;
7
f035d41b 8use rustc_arena::TypedArena;
353b0b11
FG
9use rustc_ast::Mutability;
10use rustc_data_structures::stack::ensure_sufficient_stack;
5e7ed085 11use rustc_errors::{
9c376795 12 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
5e7ed085 13};
dfeec247
XL
14use rustc_hir as hir;
15use rustc_hir::def::*;
353b0b11
FG
16use rustc_hir::def_id::LocalDefId;
17use rustc_hir::HirId;
18use rustc_middle::thir::visit::{self, Visitor};
19use rustc_middle::thir::*;
9c376795 20use rustc_middle::ty::print::with_no_trimmed_paths;
c295e0f8
XL
21use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
22use rustc_session::lint::builtin::{
23 BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
24};
dfeec247 25use rustc_session::Session;
353b0b11
FG
26use rustc_span::hygiene::DesugaringKind;
27use rustc_span::Span;
ff7c6d11 28
49aad941
FG
29pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
30 let (thir, expr) = tcx.thir_body(def_id)?;
353b0b11 31 let thir = thir.borrow();
c295e0f8 32 let pattern_arena = TypedArena::default();
f9f354fc
XL
33 let mut visitor = MatchVisitor {
34 tcx,
353b0b11 35 thir: &*thir,
f9f354fc 36 param_env: tcx.param_env(def_id),
353b0b11
FG
37 lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
38 let_source: LetSource::None,
c295e0f8 39 pattern_arena: &pattern_arena,
49aad941 40 error: Ok(()),
f9f354fc 41 };
353b0b11 42 visitor.visit_expr(&thir[expr]);
49aad941 43
353b0b11
FG
44 for param in thir.params.iter() {
45 if let Some(box ref pattern) = param.pat {
46 visitor.check_irrefutable(pattern, "function argument", None);
47 }
48 }
49aad941 49 visitor.error
ff7c6d11
XL
50}
51
5e7ed085
FG
52fn create_e0004(
53 sess: &Session,
54 sp: Span,
55 error_message: String,
56) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
c30ab7b3 57 struct_span_err!(sess, sp, E0004, "{}", &error_message)
223e47cc
LB
58}
59
c295e0f8
XL
60#[derive(PartialEq)]
61enum RefutableFlag {
62 Irrefutable,
63 Refutable,
64}
65use RefutableFlag::*;
66
353b0b11
FG
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68enum LetSource {
69 None,
70 IfLet,
71 IfLetGuard,
72 LetElse,
73 WhileLet,
74}
75
c295e0f8 76struct MatchVisitor<'a, 'p, 'tcx> {
dc9dc135 77 tcx: TyCtxt<'tcx>,
7cac9316 78 param_env: ty::ParamEnv<'tcx>,
353b0b11
FG
79 thir: &'a Thir<'tcx>,
80 lint_level: HirId,
81 let_source: LetSource,
c295e0f8 82 pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
49aad941 83 error: Result<(), ErrorGuaranteed>,
1a4d82fc
JJ
84}
85
353b0b11
FG
86impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
87 fn thir(&self) -> &'a Thir<'tcx> {
88 self.thir
89 }
90
91 #[instrument(level = "trace", skip(self))]
92 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
49aad941
FG
93 self.with_lint_level(arm.lint_level, |this| {
94 match arm.guard {
95 Some(Guard::If(expr)) => {
96 this.with_let_source(LetSource::IfLetGuard, |this| {
97 this.visit_expr(&this.thir[expr])
98 });
99 }
100 Some(Guard::IfLet(ref pat, expr)) => {
101 this.with_let_source(LetSource::IfLetGuard, |this| {
102 this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
103 this.visit_pat(pat);
104 this.visit_expr(&this.thir[expr]);
105 });
106 }
107 None => {}
a2a8927a 108 }
49aad941
FG
109 this.visit_pat(&arm.pattern);
110 this.visit_expr(&self.thir[arm.body]);
111 });
1a4d82fc 112 }
c30ab7b3 113
353b0b11
FG
114 #[instrument(level = "trace", skip(self))]
115 fn visit_expr(&mut self, ex: &Expr<'tcx>) {
116 match ex.kind {
117 ExprKind::Scope { value, lint_level, .. } => {
49aad941
FG
118 self.with_lint_level(lint_level, |this| {
119 this.visit_expr(&this.thir[value]);
120 });
353b0b11
FG
121 return;
122 }
123 ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
124 // Give a specific `let_source` for the condition.
125 let let_source = match ex.span.desugaring_kind() {
126 Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
127 _ => LetSource::IfLet,
128 };
129 self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
130 self.with_let_source(LetSource::None, |this| {
131 this.visit_expr(&this.thir[then]);
132 if let Some(else_) = else_opt {
133 this.visit_expr(&this.thir[else_]);
134 }
135 });
136 return;
137 }
138 ExprKind::Match { scrutinee, box ref arms } => {
139 let source = match ex.span.desugaring_kind() {
140 Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar,
141 Some(DesugaringKind::QuestionMark) => hir::MatchSource::TryDesugar,
142 Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
143 _ => hir::MatchSource::Normal,
144 };
145 self.check_match(scrutinee, arms, source, ex.span);
146 }
147 ExprKind::Let { box ref pat, expr } => {
148 self.check_let(pat, expr, self.let_source, ex.span);
149 }
150 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
151 self.check_let_chain(self.let_source, ex.span, lhs, rhs);
152 }
153 _ => {}
e74abb32 154 };
353b0b11 155 self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
1a4d82fc 156 }
c30ab7b3 157
353b0b11
FG
158 fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
159 let old_lint_level = self.lint_level;
160 match stmt.kind {
161 StmtKind::Let {
162 box ref pattern, initializer, else_block, lint_level, span, ..
163 } => {
164 if let LintLevel::Explicit(lint_level) = lint_level {
165 self.lint_level = lint_level;
2c00a5a8 166 }
353b0b11
FG
167
168 if let Some(initializer) = initializer && else_block.is_some() {
169 self.check_let(pattern, initializer, LetSource::LetElse, span);
ba9703b0 170 }
353b0b11
FG
171
172 if else_block.is_none() {
173 self.check_irrefutable(pattern, "local binding", Some(span));
1a4d82fc
JJ
174 }
175 }
353b0b11 176 _ => {}
c30ab7b3 177 }
353b0b11
FG
178 visit::walk_stmt(self, stmt);
179 self.lint_level = old_lint_level;
c30ab7b3 180 }
cc61c64b
XL
181}
182
c295e0f8 183impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
353b0b11
FG
184 #[instrument(level = "trace", skip(self, f))]
185 fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
186 let old_let_source = self.let_source;
187 self.let_source = let_source;
188 ensure_sufficient_stack(|| f(self));
189 self.let_source = old_let_source;
190 }
191
49aad941
FG
192 fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
193 if let LintLevel::Explicit(hir_id) = new_lint_level {
194 let old_lint_level = self.lint_level;
195 self.lint_level = hir_id;
196 f(self);
197 self.lint_level = old_lint_level;
198 } else {
199 f(self);
200 }
201 }
202
353b0b11 203 fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
74b04a01 204 pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
c295e0f8 205 check_for_bindings_named_same_as_variants(self, pat, rf);
cc61c64b 206 }
1a4d82fc 207
c295e0f8 208 fn lower_pattern(
dfeec247
XL
209 &self,
210 cx: &mut MatchCheckCtxt<'p, 'tcx>,
353b0b11 211 pattern: &Pat<'tcx>,
c295e0f8 212 ) -> &'p DeconstructedPat<'p, 'tcx> {
353b0b11 213 cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern))
dfeec247
XL
214 }
215
353b0b11 216 fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
f9f354fc
XL
217 MatchCheckCtxt {
218 tcx: self.tcx,
219 param_env: self.param_env,
220 module: self.tcx.parent_module(hir_id).to_def_id(),
221 pattern_arena: &self.pattern_arena,
353b0b11 222 refutable,
f9f354fc 223 }
dfeec247
XL
224 }
225
353b0b11
FG
226 #[instrument(level = "trace", skip(self))]
227 fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) {
228 if let LetSource::None = source {
229 return;
230 }
c295e0f8 231 self.check_patterns(pat, Refutable);
353b0b11
FG
232 let mut cx = self.new_cx(self.lint_level, true);
233 let tpat = self.lower_pattern(&mut cx, pat);
234 self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span);
94222f64
XL
235 }
236
dfeec247
XL
237 fn check_match(
238 &mut self,
353b0b11
FG
239 scrut: ExprId,
240 arms: &[ArmId],
dfeec247 241 source: hir::MatchSource,
5e7ed085 242 expr_span: Span,
dfeec247 243 ) {
353b0b11 244 let mut cx = self.new_cx(self.lint_level, true);
94222f64 245
353b0b11 246 for &arm in arms {
dfeec247 247 // Check the arm for some things unrelated to exhaustiveness.
353b0b11 248 let arm = &self.thir.arms[arm];
49aad941
FG
249 self.with_lint_level(arm.lint_level, |this| {
250 this.check_patterns(&arm.pattern, Refutable);
251 });
fc512014
XL
252 }
253
353b0b11 254 let tarms: Vec<_> = arms
f9f354fc 255 .iter()
353b0b11
FG
256 .map(|&arm| {
257 let arm = &self.thir.arms[arm];
258 let hir_id = match arm.lint_level {
259 LintLevel::Explicit(hir_id) => hir_id,
260 LintLevel::Inherited => self.lint_level,
261 };
262 let pat = self.lower_pattern(&mut cx, &arm.pattern);
263 MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }
f9f354fc
XL
264 })
265 .collect();
1a4d82fc 266
353b0b11
FG
267 let scrut = &self.thir[scrut];
268 let scrut_ty = scrut.ty;
269 let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty);
fc512014 270
c295e0f8 271 match source {
3c0e092e
XL
272 // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
273 // when the iterator is an uninhabited type. unreachable_code will trigger instead.
274 hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
9ffffee4
FG
275 hir::MatchSource::ForLoopDesugar
276 | hir::MatchSource::Normal
277 | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
c295e0f8
XL
278 // Unreachable patterns in try and await expressions occur when one of
279 // the arms are an uninhabited type. Which is OK.
280 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
281 }
f9f354fc 282
fc512014 283 // Check if the match is exhaustive.
fc512014
XL
284 let witnesses = report.non_exhaustiveness_witnesses;
285 if !witnesses.is_empty() {
353b0b11 286 if source == hir::MatchSource::ForLoopDesugar && arms.len() == 2 {
3c0e092e 287 // the for loop pattern is not irrefutable
353b0b11
FG
288 let pat = &self.thir[arms[1]].pattern;
289 // `pat` should be `Some(<pat_field>)` from a desugared for loop.
290 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
291 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
292 let [pat_field] = &subpatterns[..] else { bug!() };
293 self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
3c0e092e 294 } else {
49aad941 295 self.error = Err(non_exhaustive_match(
353b0b11 296 &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
49aad941 297 ));
3c0e092e 298 }
fc512014 299 }
c30ab7b3
SL
300 }
301
5e7ed085
FG
302 fn check_let_reachability(
303 &mut self,
304 cx: &mut MatchCheckCtxt<'p, 'tcx>,
305 pat_id: HirId,
353b0b11 306 source: LetSource,
5e7ed085
FG
307 pat: &'p DeconstructedPat<'p, 'tcx>,
308 span: Span,
309 ) {
5e7ed085 310 if is_let_irrefutable(cx, pat_id, pat) {
353b0b11 311 irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span);
5e7ed085
FG
312 }
313 }
314
353b0b11
FG
315 #[instrument(level = "trace", skip(self))]
316 fn check_let_chain(
317 &mut self,
318 let_source: LetSource,
319 top_expr_span: Span,
320 mut lhs: ExprId,
321 rhs: ExprId,
322 ) {
323 if let LetSource::None = let_source {
324 return;
325 }
5e7ed085 326
353b0b11
FG
327 // Lint level enclosing the next `lhs`.
328 let mut cur_lint_level = self.lint_level;
329
330 // Obtain the refutabilities of all exprs in the chain,
331 // and record chain members that aren't let exprs.
332 let mut chain_refutabilities = Vec::new();
333
334 let add = |expr: ExprId, mut local_lint_level| {
335 // `local_lint_level` is the lint level enclosing the pattern inside `expr`.
336 let mut expr = &self.thir[expr];
337 debug!(?expr, ?local_lint_level, "add");
338 // Fast-forward through scopes.
339 while let ExprKind::Scope { value, lint_level, .. } = expr.kind {
340 if let LintLevel::Explicit(hir_id) = lint_level {
341 local_lint_level = hir_id
5e7ed085 342 }
353b0b11
FG
343 expr = &self.thir[value];
344 }
345 debug!(?expr, ?local_lint_level, "after scopes");
346 match expr.kind {
347 ExprKind::Let { box ref pat, expr: _ } => {
348 let mut ncx = self.new_cx(local_lint_level, true);
349 let tpat = self.lower_pattern(&mut ncx, pat);
350 let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat);
351 Some((expr.span, refutable))
5e7ed085 352 }
353b0b11 353 _ => None,
5e7ed085 354 }
353b0b11 355 };
5e7ed085 356
353b0b11
FG
357 // Let chains recurse on the left, so we start by adding the rightmost.
358 chain_refutabilities.push(add(rhs, cur_lint_level));
5e7ed085 359
5e7ed085 360 loop {
353b0b11
FG
361 while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind {
362 if let LintLevel::Explicit(hir_id) = lint_level {
363 cur_lint_level = hir_id
364 }
365 lhs = value;
366 }
367 if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } =
368 self.thir[lhs].kind
5e7ed085 369 {
353b0b11
FG
370 chain_refutabilities.push(add(expr, cur_lint_level));
371 lhs = new_lhs;
5e7ed085 372 } else {
353b0b11 373 chain_refutabilities.push(add(lhs, cur_lint_level));
5e7ed085
FG
374 break;
375 }
376 }
353b0b11 377 debug!(?chain_refutabilities);
5e7ed085
FG
378 chain_refutabilities.reverse();
379
380 // Third, emit the actual warnings.
5e7ed085
FG
381 if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
382 // The entire chain is made up of irrefutable `let` statements
5e7ed085 383 irrefutable_let_patterns(
353b0b11
FG
384 self.tcx,
385 self.lint_level,
5e7ed085
FG
386 let_source,
387 chain_refutabilities.len(),
353b0b11 388 top_expr_span,
5e7ed085 389 );
353b0b11 390 return;
5e7ed085 391 }
353b0b11 392
5e7ed085
FG
393 if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
394 // The chain has a non-zero prefix of irrefutable `let` statements.
395
396 // Check if the let source is while, for there is no alternative place to put a prefix,
397 // and we shouldn't lint.
2b03887a
FG
398 // For let guards inside a match, prefixes might use bindings of the match pattern,
399 // so can't always be moved out.
400 // FIXME: Add checking whether the bindings are actually used in the prefix,
401 // and lint if they are not.
2b03887a 402 if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
5e7ed085
FG
403 // Emit the lint
404 let prefix = &chain_refutabilities[..until];
9c376795
FG
405 let span_start = prefix[0].unwrap().0;
406 let span_end = prefix.last().unwrap().unwrap().0;
407 let span = span_start.to(span_end);
408 let count = prefix.len();
353b0b11 409 self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, LeadingIrrefutableLetPatterns { count });
5e7ed085
FG
410 }
411 }
353b0b11 412
5e7ed085
FG
413 if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
414 // The chain has a non-empty suffix of irrefutable `let` statements
415 let suffix = &chain_refutabilities[from + 1..];
9c376795
FG
416 let span_start = suffix[0].unwrap().0;
417 let span_end = suffix.last().unwrap().unwrap().0;
418 let span = span_start.to(span_end);
419 let count = suffix.len();
353b0b11 420 self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, TrailingIrrefutableLetPatterns { count });
5e7ed085 421 }
5e7ed085
FG
422 }
423
353b0b11 424 #[instrument(level = "trace", skip(self))]
49aad941 425 fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
353b0b11 426 let mut cx = self.new_cx(self.lint_level, false);
e74abb32 427
353b0b11 428 let pattern = self.lower_pattern(&mut cx, pat);
c295e0f8 429 let pattern_ty = pattern.ty();
353b0b11
FG
430 let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false };
431 let report = compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty);
fc512014
XL
432
433 // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
434 // only care about exhaustiveness here.
435 let witnesses = report.non_exhaustiveness_witnesses;
436 if witnesses.is_empty() {
437 // The pattern is irrefutable.
3c0e092e 438 self.check_patterns(pat, Irrefutable);
fc512014
XL
439 return;
440 }
f9f354fc 441
353b0b11
FG
442 let inform = sp.is_some().then_some(Inform);
443 let mut let_suggestion = None;
444 let mut misc_suggestion = None;
445 let mut interpreted_as_const = None;
446 if let PatKind::Constant { .. } = pat.kind
447 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
448 {
449 // If the pattern to match is an integer literal:
450 if snippet.chars().all(|c| c.is_digit(10)) {
451 // Then give a suggestion, the user might've meant to create a binding instead.
452 misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
453 start_span: pat.span.shrink_to_lo()
5e7ed085 454 });
353b0b11
FG
455 } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') {
456 interpreted_as_const = Some(InterpretedAsConst {
457 span: pat.span,
458 variable: snippet,
459 });
460 }
461 }
462
463 if let Some(span) = sp
464 && self.tcx.sess.source_map().is_span_accessible(span)
465 && interpreted_as_const.is_none()
466 {
467 let mut bindings = vec![];
468 pat.each_binding(|name, _, _, _| bindings.push(name));
469
470 let semi_span = span.shrink_to_hi();
471 let start_span = span.shrink_to_lo();
472 let end_span = semi_span.shrink_to_lo();
473 let count = witnesses.len();
474
475 let_suggestion = Some(if bindings.is_empty() {
476 SuggestLet::If { start_span, semi_span, count }
477 } else {
478 SuggestLet::Else { end_span, count }
479 });
480 };
1a4d82fc 481
9c376795
FG
482 let adt_defined_here = try {
483 let ty = pattern_ty.peel_refs();
484 let ty::Adt(def, _) = ty.kind() else { None? };
485 let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
486 let mut variants = vec![];
e74abb32 487
9c376795
FG
488 for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
489 variants.push(Variant { span });
490 }
491 AdtDefinedHere { adt_def_span, ty, variants }
492 };
e74abb32 493
49aad941
FG
494 // Emit an extra note if the first uncovered witness would be uninhabited
495 // if we disregard visibility.
496 let witness_1_is_privately_uninhabited =
497 if cx.tcx.features().exhaustive_patterns
498 && let Some(witness_1) = witnesses.get(0)
499 && let ty::Adt(adt, substs) = witness_1.ty().kind()
500 && adt.is_enum()
501 && let Constructor::Variant(variant_index) = witness_1.ctor()
502 {
503 let variant = adt.variant(*variant_index);
504 let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs);
505 assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
506 !inhabited.apply_ignore_module(cx.tcx, cx.param_env)
507 } else {
508 false
509 };
510
511 self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
9c376795
FG
512 span: pat.span,
513 origin,
514 uncovered: Uncovered::new(pat.span, &cx, witnesses),
515 inform,
516 interpreted_as_const,
49aad941 517 witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
9c376795
FG
518 _p: (),
519 pattern_ty,
520 let_suggestion,
521 misc_suggestion,
9c376795 522 adt_defined_here,
49aad941 523 }));
e74abb32
XL
524 }
525}
526
c295e0f8
XL
527fn check_for_bindings_named_same_as_variants(
528 cx: &MatchVisitor<'_, '_, '_>,
529 pat: &Pat<'_>,
530 rf: RefutableFlag,
531) {
dfeec247 532 pat.walk_always(|p| {
353b0b11
FG
533 if let PatKind::Binding {
534 name,
535 mode: BindingMode::ByValue,
536 mutability: Mutability::Not,
537 subpattern: None,
538 ty,
539 ..
540 } = p.kind
541 && let ty::Adt(edef, _) = ty.peel_refs().kind()
5e7ed085
FG
542 && edef.is_enum()
543 && edef.variants().iter().any(|variant| {
353b0b11 544 variant.name == name && variant.ctor_kind() == Some(CtorKind::Const)
5e7ed085
FG
545 })
546 {
547 let variant_count = edef.variants().len();
9c376795
FG
548 let ty_path = with_no_trimmed_paths!({
549 cx.tcx.def_path_str(edef.did())
550 });
551 cx.tcx.emit_spanned_lint(
5e7ed085 552 BINDINGS_WITH_VARIANT_NAME,
353b0b11 553 cx.lint_level,
5e7ed085 554 p.span,
9c376795 555 BindingsWithVariantName {
5e7ed085
FG
556 // If this is an irrefutable pattern, and there's > 1 variant,
557 // then we can't actually match on this. Applying the below
558 // suggestion would produce code that breaks on `check_irrefutable`.
9c376795
FG
559 suggestion: if rf == Refutable || variant_count == 1 {
560 Some(p.span)
561 } else { None },
562 ty_path,
353b0b11 563 name,
5e7ed085
FG
564 },
565 )
1a4d82fc 566 }
1a4d82fc
JJ
567 });
568}
569
c30ab7b3 570/// Checks for common cases of "catchall" patterns that may not be intended as such.
c295e0f8
XL
571fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
572 use Constructor::*;
573 match pat.ctor() {
574 Wildcard => true,
575 Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
e74abb32 576 _ => false,
c30ab7b3 577 }
1a4d82fc
JJ
578}
579
dfeec247 580fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
9c376795
FG
581 tcx.emit_spanned_lint(
582 UNREACHABLE_PATTERNS,
583 id,
584 span,
585 UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
586 );
dfeec247
XL
587}
588
5e7ed085
FG
589fn irrefutable_let_patterns(
590 tcx: TyCtxt<'_>,
591 id: HirId,
592 source: LetSource,
593 count: usize,
594 span: Span,
595) {
94222f64 596 macro_rules! emit_diag {
9c376795
FG
597 ($lint:tt) => {{
598 tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
94222f64
XL
599 }};
600 }
601
2b03887a 602 match source {
353b0b11 603 LetSource::None => bug!(),
9c376795
FG
604 LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
605 LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
606 LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
607 LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
608 }
dfeec247
XL
609}
610
5e7ed085 611fn is_let_irrefutable<'p, 'tcx>(
60c5eb7d 612 cx: &mut MatchCheckCtxt<'p, 'tcx>,
fc512014 613 pat_id: HirId,
c295e0f8 614 pat: &'p DeconstructedPat<'p, 'tcx>,
5e7ed085 615) -> bool {
fc512014 616 let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
c295e0f8
XL
617 let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
618
619 // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
620 // This also reports unreachable sub-patterns though, so we can't just replace it with an
621 // `is_uninhabited` check.
622 report_arm_reachability(&cx, &report);
fc512014 623
5e7ed085
FG
624 // If the list of witnesses is empty, the match is exhaustive,
625 // i.e. the `if let` pattern is irrefutable.
626 report.non_exhaustiveness_witnesses.is_empty()
fc512014
XL
627}
628
629/// Report unreachable arms, if any.
c295e0f8 630fn report_arm_reachability<'p, 'tcx>(
fc512014
XL
631 cx: &MatchCheckCtxt<'p, 'tcx>,
632 report: &UsefulnessReport<'p, 'tcx>,
c295e0f8 633) {
6a06907d 634 use Reachability::*;
c30ab7b3 635 let mut catchall = None;
c295e0f8 636 for (arm, is_useful) in report.arm_usefulness.iter() {
fc512014 637 match is_useful {
c295e0f8 638 Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
6a06907d 639 Reachable(unreachables) if unreachables.is_empty() => {}
fc512014 640 // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
6a06907d
XL
641 Reachable(unreachables) => {
642 let mut unreachables = unreachables.clone();
29967ef6
XL
643 // Emit lints in the order in which they occur in the file.
644 unreachables.sort_unstable();
645 for span in unreachables {
fc512014 646 unreachable_pattern(cx.tcx, span, arm.hir_id, None);
c30ab7b3 647 }
223e47cc 648 }
60c5eb7d 649 }
fc512014 650 if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
c295e0f8 651 catchall = Some(arm.pat.span());
223e47cc
LB
652 }
653 }
654}
655
fc512014
XL
656/// Report that a match is not exhaustive.
657fn non_exhaustive_match<'p, 'tcx>(
658 cx: &MatchCheckCtxt<'p, 'tcx>,
353b0b11 659 thir: &Thir<'tcx>,
532ac7d7
XL
660 scrut_ty: Ty<'tcx>,
661 sp: Span,
c295e0f8 662 witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
353b0b11 663 arms: &[ArmId],
5e7ed085 664 expr_span: Span,
49aad941 665) -> ErrorGuaranteed {
5e7ed085 666 let is_empty_match = arms.is_empty();
1b1a35ee 667 let non_empty_enum = match scrut_ty.kind() {
5e7ed085 668 ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
60c5eb7d
XL
669 _ => false,
670 };
671 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
672 // informative.
673 let mut err;
5e7ed085 674 let pattern;
9c376795 675 let patterns_len;
60c5eb7d 676 if is_empty_match && !non_empty_enum {
49aad941 677 return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
9c376795
FG
678 cx,
679 expr_span,
680 span: sp,
681 ty: scrut_ty,
682 });
60c5eb7d 683 } else {
9c376795 684 // FIXME: migration of this diagnostic will require list support
c295e0f8 685 let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
60c5eb7d
XL
686 err = create_e0004(
687 cx.tcx.sess,
688 sp,
689 format!("non-exhaustive patterns: {} not covered", joined_patterns),
690 );
691 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
5e7ed085
FG
692 patterns_len = witnesses.len();
693 pattern = if witnesses.len() < 4 {
694 witnesses
695 .iter()
696 .map(|witness| witness.to_pat(cx).to_string())
697 .collect::<Vec<String>>()
698 .join(" | ")
699 } else {
700 "_".to_string()
701 };
60c5eb7d
XL
702 };
703
49aad941
FG
704 let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(),
705 ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
17df50a5 706
e1599b0c 707 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
49aad941 708 err.note(format!(
17df50a5
XL
709 "the matched value is of type `{}`{}",
710 scrut_ty,
711 if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
712 ));
f035d41b
XL
713 if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
714 && !is_empty_match
715 && witnesses.len() == 1
c295e0f8 716 && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
f035d41b 717 {
49aad941 718 err.note(format!(
5e7ed085
FG
719 "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
720 exhaustively",
f035d41b
XL
721 scrut_ty,
722 ));
fc512014 723 if cx.tcx.sess.is_nightly_build() {
49aad941 724 err.help(format!(
5e7ed085
FG
725 "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
726 enable precise `{}` matching",
f035d41b
XL
727 scrut_ty,
728 ));
729 }
730 }
5869c6ff 731 if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
487cf647 732 if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
5869c6ff
XL
733 err.note("references are always considered inhabited");
734 }
735 }
5e7ed085
FG
736
737 let mut suggestion = None;
738 let sm = cx.tcx.sess.source_map();
739 match arms {
923072b8 740 [] if sp.eq_ctxt(expr_span) => {
5e7ed085
FG
741 // Get the span for the empty match body `{}`.
742 let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
743 (format!("\n{}", snippet), " ")
744 } else {
745 (" ".to_string(), "")
746 };
747 suggestion = Some((
748 sp.shrink_to_hi().with_hi(expr_span.hi()),
749 format!(
750 " {{{indentation}{more}{pattern} => todo!(),{indentation}}}",
751 indentation = indentation,
752 more = more,
753 pattern = pattern,
754 ),
755 ));
756 }
757 [only] => {
353b0b11 758 let only = &thir[*only];
923072b8
FG
759 let (pre_indentation, is_multiline) = if let Some(snippet) = sm.indentation_before(only.span)
760 && let Ok(with_trailing) = sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
761 && sm.is_multiline(with_trailing)
762 {
763 (format!("\n{}", snippet), true)
764 } else {
765 (" ".to_string(), false)
766 };
353b0b11
FG
767 let only_body = &thir[only.body];
768 let comma = if matches!(only_body.kind, ExprKind::Block { .. })
769 && only.span.eq_ctxt(only_body.span)
923072b8
FG
770 && is_multiline
771 {
772 ""
5e7ed085 773 } else {
923072b8 774 ","
5e7ed085 775 };
5e7ed085
FG
776 suggestion = Some((
777 only.span.shrink_to_hi(),
778 format!("{}{}{} => todo!()", comma, pre_indentation, pattern),
779 ));
780 }
353b0b11
FG
781 [.., prev, last] => {
782 let prev = &thir[*prev];
783 let last = &thir[*last];
784 if prev.span.eq_ctxt(last.span) {
785 let last_body = &thir[last.body];
786 let comma = if matches!(last_body.kind, ExprKind::Block { .. })
787 && last.span.eq_ctxt(last_body.span)
788 {
789 ""
790 } else {
791 ","
792 };
793 let spacing = if sm.is_multiline(prev.span.between(last.span)) {
794 sm.indentation_before(last.span).map(|indent| format!("\n{indent}"))
795 } else {
796 Some(" ".to_string())
797 };
798 if let Some(spacing) = spacing {
799 suggestion = Some((
800 last.span.shrink_to_hi(),
801 format!("{}{}{} => todo!()", comma, spacing, pattern),
802 ));
803 }
5e7ed085
FG
804 }
805 }
806 _ => {}
807 }
808
809 let msg = format!(
810 "ensure that all possible cases are being handled by adding a match arm with a wildcard \
811 pattern{}{}",
812 if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() {
813 ", a match arm with multiple or-patterns"
814 } else {
815 // we are either not suggesting anything, or suggesting `_`
816 ""
817 },
818 match patterns_len {
819 // non-exhaustive enum case
820 0 if suggestion.is_some() => " as shown",
821 0 => "",
822 1 if suggestion.is_some() => " or an explicit pattern as shown",
823 1 => " or an explicit pattern",
824 _ if suggestion.is_some() => " as shown, or multiple match arms",
825 _ => " or multiple match arms",
826 },
827 );
828 if let Some((span, sugg)) = suggestion {
49aad941 829 err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
5e7ed085 830 } else {
49aad941 831 err.help(msg);
5e7ed085 832 }
49aad941 833 err.emit()
e1599b0c 834}
7cac9316 835
923072b8 836pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
c295e0f8
XL
837 cx: &MatchCheckCtxt<'p, 'tcx>,
838 witnesses: &[DeconstructedPat<'p, 'tcx>],
839) -> String {
e1599b0c 840 const LIMIT: usize = 3;
c295e0f8 841 let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
e1599b0c
XL
842 match witnesses {
843 [] => bug!(),
c295e0f8 844 [witness] => format!("`{}`", witness.to_pat(cx)),
e1599b0c 845 [head @ .., tail] if head.len() < LIMIT => {
c295e0f8
XL
846 let head: Vec<_> = head.iter().map(pat_to_str).collect();
847 format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
1a4d82fc 848 }
e1599b0c
XL
849 _ => {
850 let (head, tail) = witnesses.split_at(LIMIT);
c295e0f8 851 let head: Vec<_> = head.iter().map(pat_to_str).collect();
e1599b0c 852 format!("`{}` and {} more", head.join("`, `"), tail.len())
9fa01778 853 }
1a4d82fc
JJ
854 }
855}
856
923072b8 857pub(crate) fn pattern_not_covered_label(
c295e0f8
XL
858 witnesses: &[DeconstructedPat<'_, '_>],
859 joined_patterns: &str,
860) -> String {
60c5eb7d 861 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
e1599b0c
XL
862}
863
864/// Point at the definition of non-covered `enum` variants.
c295e0f8
XL
865fn adt_defined_here<'p, 'tcx>(
866 cx: &MatchCheckCtxt<'p, 'tcx>,
5e7ed085 867 err: &mut Diagnostic,
c295e0f8
XL
868 ty: Ty<'tcx>,
869 witnesses: &[DeconstructedPat<'p, 'tcx>],
e1599b0c
XL
870) {
871 let ty = ty.peel_refs();
1b1a35ee 872 if let ty::Adt(def, _) = ty.kind() {
5e7ed085
FG
873 let mut spans = vec![];
874 if witnesses.len() < 5 {
875 for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) {
876 spans.push(sp);
e1599b0c
XL
877 }
878 }
5e7ed085
FG
879 let def_span = cx
880 .tcx
881 .hir()
882 .get_if_local(def.did())
883 .and_then(|node| node.ident())
884 .map(|ident| ident.span)
885 .unwrap_or_else(|| cx.tcx.def_span(def.did()));
886 let mut span: MultiSpan =
887 if spans.is_empty() { def_span.into() } else { spans.clone().into() };
888
064997fb 889 span.push_span_label(def_span, "");
5e7ed085 890 for pat in spans {
064997fb 891 span.push_span_label(pat, "not covered");
5e7ed085 892 }
49aad941 893 err.span_note(span, format!("`{}` defined here", ty));
e1599b0c
XL
894 }
895}
896
c295e0f8
XL
897fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
898 cx: &MatchCheckCtxt<'p, 'tcx>,
5e7ed085 899 def: AdtDef<'tcx>,
c295e0f8
XL
900 patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
901) -> Vec<Span> {
902 use Constructor::*;
532ac7d7 903 let mut covered = vec![];
c295e0f8
XL
904 for pattern in patterns {
905 if let Variant(variant_index) = pattern.ctor() {
5e7ed085
FG
906 if let ty::Adt(this_def, _) = pattern.ty().kind() && this_def.did() != def.did() {
907 continue;
532ac7d7 908 }
5e7ed085 909 let sp = def.variant(*variant_index).ident(cx.tcx).span;
c295e0f8
XL
910 if covered.contains(&sp) {
911 // Don't point at variants that have already been covered due to other patterns to avoid
912 // visual clutter.
913 continue;
914 }
915 covered.push(sp);
532ac7d7 916 }
c295e0f8 917 covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
532ac7d7
XL
918 }
919 covered
920}
921
74b04a01 922/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
74b04a01 923/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
dfeec247
XL
924///
925/// For example, this would reject:
926/// - `ref x @ Some(ref mut y)`,
74b04a01
XL
927/// - `ref mut x @ Some(ref y)`,
928/// - `ref mut x @ Some(ref mut y)`,
929/// - `ref mut? x @ Some(y)`, and
930/// - `x @ Some(ref mut? y)`.
dfeec247
XL
931///
932/// This analysis is *not* subsumed by NLL.
353b0b11 933fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
74b04a01 934 // Extract `sub` in `binding @ sub`.
353b0b11
FG
935 let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { return };
936
937 let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
dfeec247 938
74b04a01 939 let sess = cx.tcx.sess;
1a4d82fc 940
74b04a01 941 // Get the binding move, extract the mutability if by-ref.
353b0b11
FG
942 let mut_outer = match mode {
943 BindingMode::ByValue if is_binding_by_move(ty) => {
74b04a01
XL
944 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
945 let mut conflicts_ref = Vec::new();
353b0b11
FG
946 sub.each_binding(|_, mode, _, span| match mode {
947 BindingMode::ByValue => {}
948 BindingMode::ByRef(_) => conflicts_ref.push(span),
74b04a01
XL
949 });
950 if !conflicts_ref.is_empty() {
9c376795 951 sess.emit_err(BorrowOfMovedValue {
353b0b11 952 binding_span: pat.span,
9c376795 953 conflicts_ref,
74b04a01 954 name,
353b0b11
FG
955 ty,
956 suggest_borrowing: Some(pat.span.shrink_to_lo()),
9c376795 957 });
dfeec247 958 }
74b04a01
XL
959 return;
960 }
353b0b11
FG
961 BindingMode::ByValue => return,
962 BindingMode::ByRef(m) => m.mutability(),
74b04a01 963 };
dfeec247 964
74b04a01
XL
965 // We now have `ref $mut_outer binding @ sub` (semantically).
966 // Recurse into each binding in `sub` and find mutability or move conflicts.
967 let mut conflicts_move = Vec::new();
968 let mut conflicts_mut_mut = Vec::new();
969 let mut conflicts_mut_ref = Vec::new();
353b0b11
FG
970 sub.each_binding(|name, mode, ty, span| {
971 match mode {
972 BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
9ffffee4
FG
973 // Both sides are `ref`.
974 (Mutability::Not, Mutability::Not) => {}
975 // 2x `ref mut`.
976 (Mutability::Mut, Mutability::Mut) => {
977 conflicts_mut_mut.push(Conflict::Mut { span, name })
978 }
979 (Mutability::Not, Mutability::Mut) => {
980 conflicts_mut_ref.push(Conflict::Mut { span, name })
981 }
982 (Mutability::Mut, Mutability::Not) => {
983 conflicts_mut_ref.push(Conflict::Ref { span, name })
984 }
74b04a01 985 },
353b0b11 986 BindingMode::ByValue if is_binding_by_move(ty) => {
9ffffee4 987 conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
dfeec247 988 }
353b0b11 989 BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
dfeec247
XL
990 }
991 });
74b04a01 992
9ffffee4
FG
993 let report_mut_mut = !conflicts_mut_mut.is_empty();
994 let report_mut_ref = !conflicts_mut_ref.is_empty();
995 let report_move_conflict = !conflicts_move.is_empty();
996
353b0b11
FG
997 let mut occurrences = match mut_outer {
998 Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
999 Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
9ffffee4 1000 };
353b0b11
FG
1001 occurrences.extend(conflicts_mut_mut);
1002 occurrences.extend(conflicts_mut_ref);
1003 occurrences.extend(conflicts_move);
9ffffee4 1004
74b04a01 1005 // Report errors if any.
9ffffee4 1006 if report_mut_mut {
74b04a01 1007 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
353b0b11 1008 sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
9ffffee4 1009 } else if report_mut_ref {
74b04a01 1010 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
9ffffee4
FG
1011 match mut_outer {
1012 Mutability::Mut => {
353b0b11 1013 sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
9ffffee4
FG
1014 }
1015 Mutability::Not => {
353b0b11 1016 sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
9ffffee4 1017 }
74b04a01 1018 };
9ffffee4 1019 } else if report_move_conflict {
74b04a01 1020 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
353b0b11 1021 sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
064997fb 1022 }
5099ac24 1023}