]>
Commit | Line | Data |
---|---|---|
c295e0f8 | 1 | use super::deconstruct_pat::{Constructor, DeconstructedPat}; |
fc512014 | 2 | use super::usefulness::{ |
c295e0f8 | 3 | compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, |
fc512014 | 4 | }; |
223e47cc | 5 | |
9c376795 FG |
6 | use crate::errors::*; |
7 | ||
f035d41b | 8 | use rustc_arena::TypedArena; |
353b0b11 FG |
9 | use rustc_ast::Mutability; |
10 | use rustc_data_structures::stack::ensure_sufficient_stack; | |
5e7ed085 | 11 | use rustc_errors::{ |
9c376795 | 12 | struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, |
5e7ed085 | 13 | }; |
dfeec247 XL |
14 | use rustc_hir as hir; |
15 | use rustc_hir::def::*; | |
353b0b11 FG |
16 | use rustc_hir::def_id::LocalDefId; |
17 | use rustc_hir::HirId; | |
18 | use rustc_middle::thir::visit::{self, Visitor}; | |
19 | use rustc_middle::thir::*; | |
9c376795 | 20 | use rustc_middle::ty::print::with_no_trimmed_paths; |
c295e0f8 XL |
21 | use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; |
22 | use rustc_session::lint::builtin::{ | |
23 | BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, | |
24 | }; | |
dfeec247 | 25 | use rustc_session::Session; |
353b0b11 FG |
26 | use rustc_span::hygiene::DesugaringKind; |
27 | use rustc_span::Span; | |
ff7c6d11 | 28 | |
49aad941 FG |
29 | pub(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 |
52 | fn 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)] |
61 | enum RefutableFlag { | |
62 | Irrefutable, | |
63 | Refutable, | |
64 | } | |
65 | use RefutableFlag::*; | |
66 | ||
353b0b11 FG |
67 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
68 | enum LetSource { | |
69 | None, | |
70 | IfLet, | |
71 | IfLetGuard, | |
72 | LetElse, | |
73 | WhileLet, | |
74 | } | |
75 | ||
c295e0f8 | 76 | struct 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 |
86 | impl<'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 | 183 | impl<'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 |
527 | fn 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 |
571 | fn 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 | 580 | fn 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 |
589 | fn 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 | 611 | fn 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 | 630 | fn 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. |
657 | fn 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 | 836 | pub(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 | 857 | pub(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 |
865 | fn 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 |
897 | fn 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 | 933 | fn 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 | } |