1use rustc_arena::{DroplessArena, TypedArena};
2use rustc_ast::Mutability;
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_data_structures::stack::ensure_sufficient_stack;
5use rustc_errors::codes::*;
6use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, msg, struct_span_code_err};
7use rustc_hir::def::*;
8use rustc_hir::def_id::{DefId, LocalDefId};
9use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_middle::bug;
12use rustc_middle::thir::visit::Visitor;
13use rustc_middle::thir::*;
14use rustc_middle::ty::print::with_no_trimmed_paths;
15use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
16use rustc_pattern_analysis::errors::Uncovered;
17use rustc_pattern_analysis::rustc::{
18 Constructor, DeconstructedPat, MatchArm, RedundancyExplanation, RevealedTy,
19 RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, WitnessPat,
20};
21use rustc_session::lint::builtin::{
22 BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
23};
24use rustc_span::edit_distance::find_best_match_for_name;
25use rustc_span::hygiene::DesugaringKind;
26use rustc_span::{Ident, Span};
27use rustc_trait_selection::infer::InferCtxtExt;
28use tracing::instrument;
29
30use crate::errors::*;
31
32pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
33 let typeck_results = tcx.typeck(def_id);
34 let (thir, expr) = tcx.thir_body(def_id)?;
35 let thir = thir.borrow();
36 let pattern_arena = TypedArena::default();
37 let dropless_arena = DroplessArena::default();
38 let mut visitor = MatchVisitor {
39 tcx,
40 thir: &*thir,
41 typeck_results,
42 typing_env: ty::TypingEnv::non_body_analysis(tcx, def_id),
44 hir_source: tcx.local_def_id_to_hir_id(def_id),
45 let_source: LetSource::None,
46 pattern_arena: &pattern_arena,
47 dropless_arena: &dropless_arena,
48 error: Ok(()),
49 };
50 visitor.visit_expr(&thir[expr]);
51
52 let origin = match tcx.def_kind(def_id) {
53 DefKind::AssocFn | DefKind::Fn => "function argument",
54 DefKind::Closure => "closure argument",
55 _ if thir.params.is_empty() => "",
58 kind => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected function parameters in THIR: {0:?} {1:?}",
kind, def_id))bug!("unexpected function parameters in THIR: {kind:?} {def_id:?}"),
59 };
60
61 for param in thir.params.iter() {
62 if let Some(ref pattern) = param.pat {
63 visitor.check_binding_is_irrefutable(pattern, origin, None, None);
64 }
65 }
66 visitor.error
67}
68
69#[derive(#[automatically_derived]
impl ::core::fmt::Debug for RefutableFlag {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
RefutableFlag::Irrefutable => "Irrefutable",
RefutableFlag::Refutable => "Refutable",
})
}
}Debug, #[automatically_derived]
impl ::core::marker::Copy for RefutableFlag { }Copy, #[automatically_derived]
impl ::core::clone::Clone for RefutableFlag {
#[inline]
fn clone(&self) -> RefutableFlag { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for RefutableFlag {
#[inline]
fn eq(&self, other: &RefutableFlag) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
70enum RefutableFlag {
71 Irrefutable,
72 Refutable,
73}
74use RefutableFlag::*;
75
76#[derive(#[automatically_derived]
impl ::core::clone::Clone for LetSource {
#[inline]
fn clone(&self) -> LetSource { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LetSource { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for LetSource {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
LetSource::None => "None",
LetSource::PlainLet => "PlainLet",
LetSource::IfLet => "IfLet",
LetSource::IfLetGuard => "IfLetGuard",
LetSource::LetElse => "LetElse",
LetSource::WhileLet => "WhileLet",
LetSource::Else => "Else",
LetSource::ElseIfLet => "ElseIfLet",
})
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for LetSource {
#[inline]
fn eq(&self, other: &LetSource) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LetSource {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
77enum LetSource {
78 None,
79 PlainLet,
80 IfLet,
81 IfLetGuard,
82 LetElse,
83 WhileLet,
84 Else,
85 ElseIfLet,
86}
87
88struct MatchVisitor<'p, 'tcx> {
89 tcx: TyCtxt<'tcx>,
90 typing_env: ty::TypingEnv<'tcx>,
91 typeck_results: &'tcx ty::TypeckResults<'tcx>,
92 thir: &'p Thir<'tcx>,
93 hir_source: HirId,
94 let_source: LetSource,
95 pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
96 dropless_arena: &'p DroplessArena,
97 error: Result<(), ErrorGuaranteed>,
101}
102
103impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
105 fn thir(&self) -> &'p Thir<'tcx> {
106 self.thir
107 }
108
109 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("visit_arm",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(109u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["arm"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&arm)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
self.with_hir_source(arm.hir_id,
|this|
{
if let Some(expr) = arm.guard {
this.with_let_source(LetSource::IfLetGuard,
|this| { this.visit_expr(&this.thir[expr]) });
}
this.visit_pat(&arm.pattern);
this.visit_expr(&self.thir[arm.body]);
});
}
}
}#[instrument(level = "trace", skip(self))]
110 fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
111 self.with_hir_source(arm.hir_id, |this| {
112 if let Some(expr) = arm.guard {
113 this.with_let_source(LetSource::IfLetGuard, |this| {
114 this.visit_expr(&this.thir[expr])
115 });
116 }
117 this.visit_pat(&arm.pattern);
118 this.visit_expr(&self.thir[arm.body]);
119 });
120 }
121
122 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("visit_expr",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(122u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["ex"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ex)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
match ex.kind {
ExprKind::Scope { value, hir_id, .. } => {
self.with_hir_source(hir_id,
|this| { this.visit_expr(&this.thir[value]); });
return;
}
ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
let let_source =
match ex.span.desugaring_kind() {
Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
_ =>
match self.let_source {
LetSource::Else => LetSource::ElseIfLet,
_ => LetSource::IfLet,
},
};
self.with_let_source(let_source,
|this| this.visit_expr(&self.thir[cond]));
self.with_let_source(LetSource::None,
|this| { this.visit_expr(&this.thir[then]); });
if let Some(else_) = else_opt {
self.with_let_source(LetSource::Else,
|this| { this.visit_expr(&this.thir[else_]) });
}
return;
}
ExprKind::Match { scrutinee, ref arms, match_source } => {
self.check_match(scrutinee, arms, match_source, ex.span);
}
ExprKind::LoopMatch {
match_data: LoopMatchMatchData {
scrutinee, ref arms, span
}, .. } => {
self.check_match(scrutinee, arms, MatchSource::Normal,
span);
}
ExprKind::Let { ref pat, expr } => {
self.check_let(pat, Some(expr), ex.span, None);
}
ExprKind::LogicalOp { op: LogicalOp::And, .. } if
!#[allow(non_exhaustive_omitted_patterns)] match self.let_source
{
LetSource::None => true,
_ => false,
} => {
let mut chain_refutabilities = Vec::new();
let Ok(()) =
self.visit_land(ex,
&mut chain_refutabilities) else { return };
if let [Some((_, Irrefutable))] = chain_refutabilities[..] {
self.lint_single_let(ex.span, None, None);
}
return;
}
_ => {}
};
self.with_let_source(LetSource::None,
|this| visit::walk_expr(this, ex));
}
}
}#[instrument(level = "trace", skip(self))]
123 fn visit_expr(&mut self, ex: &'p Expr<'tcx>) {
124 match ex.kind {
125 ExprKind::Scope { value, hir_id, .. } => {
126 self.with_hir_source(hir_id, |this| {
127 this.visit_expr(&this.thir[value]);
128 });
129 return;
130 }
131 ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
132 let let_source = match ex.span.desugaring_kind() {
134 Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
135 _ => match self.let_source {
136 LetSource::Else => LetSource::ElseIfLet,
137 _ => LetSource::IfLet,
138 },
139 };
140 self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
141 self.with_let_source(LetSource::None, |this| {
142 this.visit_expr(&this.thir[then]);
143 });
144 if let Some(else_) = else_opt {
145 self.with_let_source(LetSource::Else, |this| {
146 this.visit_expr(&this.thir[else_])
147 });
148 }
149 return;
150 }
151 ExprKind::Match { scrutinee, ref arms, match_source } => {
152 self.check_match(scrutinee, arms, match_source, ex.span);
153 }
154 ExprKind::LoopMatch {
155 match_data: LoopMatchMatchData { scrutinee, ref arms, span },
156 ..
157 } => {
158 self.check_match(scrutinee, arms, MatchSource::Normal, span);
159 }
160 ExprKind::Let { ref pat, expr } => {
161 self.check_let(pat, Some(expr), ex.span, None);
162 }
163 ExprKind::LogicalOp { op: LogicalOp::And, .. }
164 if !matches!(self.let_source, LetSource::None) =>
165 {
166 let mut chain_refutabilities = Vec::new();
167 let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
168 if let [Some((_, Irrefutable))] = chain_refutabilities[..] {
170 self.lint_single_let(ex.span, None, None);
171 }
172 return;
173 }
174 _ => {}
175 };
176 self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
177 }
178
179 fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
180 match stmt.kind {
181 StmtKind::Let { ref pattern, initializer, else_block, hir_id, span, .. } => {
182 self.with_hir_source(hir_id, |this| {
183 let let_source =
184 if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
185 let else_span = else_block.map(|bid| this.thir.blocks[bid].span);
186 this.with_let_source(let_source, |this| {
187 this.check_let(pattern, initializer, span, else_span)
188 });
189 visit::walk_stmt(this, stmt);
190 });
191 }
192 StmtKind::Expr { .. } => {
193 visit::walk_stmt(self, stmt);
194 }
195 }
196 }
197}
198
199impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
200 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("with_let_source",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(200u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["let_source"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_source)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
let old_let_source = self.let_source;
self.let_source = let_source;
ensure_sufficient_stack(|| f(self));
self.let_source = old_let_source;
}
}
}#[instrument(level = "trace", skip(self, f))]
201 fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
202 let old_let_source = self.let_source;
203 self.let_source = let_source;
204 ensure_sufficient_stack(|| f(self));
205 self.let_source = old_let_source;
206 }
207
208 fn with_hir_source<T>(&mut self, new_hir_source: HirId, f: impl FnOnce(&mut Self) -> T) -> T {
209 let old_hir_source = self.hir_source;
210 self.hir_source = new_hir_source;
211 let ret = f(self);
212 self.hir_source = old_hir_source;
213 ret
214 }
215
216 fn visit_land(
219 &mut self,
220 ex: &'p Expr<'tcx>,
221 accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
222 ) -> Result<(), ErrorGuaranteed> {
223 match ex.kind {
224 ExprKind::Scope { value, hir_id, .. } => {
225 self.with_hir_source(hir_id, |this| this.visit_land(&this.thir[value], accumulator))
226 }
227 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
228 let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
230 let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
231 accumulator.push(res_rhs);
232 res_lhs
233 }
234 _ => {
235 let res = self.visit_land_rhs(ex)?;
236 accumulator.push(res);
237 Ok(())
238 }
239 }
240 }
241
242 fn visit_land_rhs(
246 &mut self,
247 ex: &'p Expr<'tcx>,
248 ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
249 match ex.kind {
250 ExprKind::Scope { value, hir_id, .. } => {
251 self.with_hir_source(hir_id, |this| this.visit_land_rhs(&this.thir[value]))
252 }
253 ExprKind::Let { ref pat, expr } => {
254 let expr = &self.thir()[expr];
255 self.with_let_source(LetSource::None, |this| {
256 this.visit_expr(expr);
257 });
258 Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
259 }
260 _ => {
261 self.with_let_source(LetSource::None, |this| {
262 this.visit_expr(ex);
263 });
264 Ok(None)
265 }
266 }
267 }
268
269 fn lower_pattern(
270 &mut self,
271 cx: &PatCtxt<'p, 'tcx>,
272 pat: &'p Pat<'tcx>,
273 ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
274 if let Err(err) = pat.pat_error_reported() {
275 self.error = Err(err);
276 Err(err)
277 } else {
278 let refutable = if cx.refutable { Refutable } else { Irrefutable };
280 let mut err = Ok(());
281 pat.walk_always(|pat| {
282 check_borrow_conflicts_in_at_patterns(self, pat);
283 check_for_bindings_named_same_as_variants(self, pat, refutable);
284 err = err.and(check_never_pattern(cx, pat));
285 });
286 err?;
287 Ok(self.pattern_arena.alloc(cx.lower_pat(pat)))
288 }
289 }
290
291 fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
294 use ExprKind::*;
295 match &scrutinee.kind {
296 Deref { .. } => false,
299 Field { lhs, .. } => {
301 let lhs = &self.thir()[*lhs];
302 match lhs.ty.kind() {
303 ty::Adt(def, _) if def.is_union() => false,
304 _ => self.is_known_valid_scrutinee(lhs),
305 }
306 }
307 Index { lhs, .. } => {
309 let lhs = &self.thir()[*lhs];
310 self.is_known_valid_scrutinee(lhs)
311 }
312
313 Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
315
316 NeverToAny { source }
318 | Cast { source }
319 | Use { source }
320 | PointerCoercion { source, .. }
321 | PlaceTypeAscription { source, .. }
322 | ValueTypeAscription { source, .. }
323 | PlaceUnwrapUnsafeBinder { source }
324 | ValueUnwrapUnsafeBinder { source }
325 | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
326
327 Become { .. }
329 | Break { .. }
330 | Continue { .. }
331 | ConstContinue { .. }
332 | Return { .. } => true,
333
334 Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
336
337 RawBorrow { .. }
339 | Adt { .. }
340 | Array { .. }
341 | Binary { .. }
342 | Block { .. }
343 | Borrow { .. }
344 | Call { .. }
345 | ByUse { .. }
346 | Closure { .. }
347 | ConstBlock { .. }
348 | ConstParam { .. }
349 | If { .. }
350 | Literal { .. }
351 | LogicalOp { .. }
352 | Loop { .. }
353 | LoopMatch { .. }
354 | Match { .. }
355 | NamedConst { .. }
356 | NonHirLiteral { .. }
357 | Repeat { .. }
358 | StaticRef { .. }
359 | ThreadLocalRef { .. }
360 | Tuple { .. }
361 | Unary { .. }
362 | UpvarRef { .. }
363 | VarRef { .. }
364 | ZstLiteral { .. }
365 | Yield { .. }
366 | Reborrow { .. } => true,
367 }
368 }
369
370 fn new_cx(
371 &self,
372 refutability: RefutableFlag,
373 whole_match_span: Option<Span>,
374 scrutinee: Option<&Expr<'tcx>>,
375 scrut_span: Span,
376 ) -> PatCtxt<'p, 'tcx> {
377 let refutable = match refutability {
378 Irrefutable => false,
379 Refutable => true,
380 };
381 let known_valid_scrutinee =
384 scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
385 PatCtxt {
386 tcx: self.tcx,
387 typeck_results: self.typeck_results,
388 typing_env: self.typing_env,
389 module: self.tcx.parent_module(self.hir_source).to_def_id(),
390 dropless_arena: self.dropless_arena,
391 match_lint_level: self.hir_source,
392 whole_match_span,
393 scrut_span,
394 refutable,
395 known_valid_scrutinee,
396 internal_state: Default::default(),
397 }
398 }
399
400 fn analyze_patterns(
401 &mut self,
402 cx: &PatCtxt<'p, 'tcx>,
403 arms: &[MatchArm<'p, 'tcx>],
404 scrut_ty: Ty<'tcx>,
405 ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
406 let report =
407 rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
408 self.error = Err(err);
409 err
410 })?;
411
412 for (arm, is_useful) in report.arm_usefulness.iter() {
414 if let Usefulness::Useful(redundant_subpats) = is_useful
415 && !redundant_subpats.is_empty()
416 {
417 let mut redundant_subpats = redundant_subpats.clone();
418 redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
420 for (pat, explanation) in redundant_subpats {
421 report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
422 }
423 }
424 }
425 Ok(report)
426 }
427
428 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("check_let",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(428u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["pat", "scrutinee",
"span", "else_span"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrutinee)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&span)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&else_span)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
if !(self.let_source != LetSource::None) {
::core::panicking::panic("assertion failed: self.let_source != LetSource::None")
};
let scrut = scrutinee.map(|id| &self.thir[id]);
if let LetSource::PlainLet = self.let_source {
self.check_binding_is_irrefutable(pat, "local binding", scrut,
Some(span));
} else if let Ok(Irrefutable) =
self.is_let_irrefutable(pat, scrut) {
if span.from_expansion() {
self.lint_single_let(span, None, None);
return;
}
let let_else_span =
self.check_irrefutable_option_some(pat, scrut, span);
let sm = self.tcx.sess.source_map();
let next_token_start =
sm.span_extend_while_whitespace(span.clone()).hi();
let line_span =
sm.span_extend_to_line(span.clone()).with_lo(next_token_start);
let else_keyword_span = sm.span_until_whitespace(line_span);
self.lint_single_let(span, Some(else_keyword_span),
let_else_span);
}
}
}
}#[instrument(level = "trace", skip(self))]
429 fn check_let(
430 &mut self,
431 pat: &'p Pat<'tcx>,
432 scrutinee: Option<ExprId>,
433 span: Span,
434 else_span: Option<Span>,
435 ) {
436 assert!(self.let_source != LetSource::None);
437 let scrut = scrutinee.map(|id| &self.thir[id]);
438 if let LetSource::PlainLet = self.let_source {
439 self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span));
440 } else if let Ok(Irrefutable) = self.is_let_irrefutable(pat, scrut) {
441 if span.from_expansion() {
442 self.lint_single_let(span, None, None);
443 return;
444 }
445 let let_else_span = self.check_irrefutable_option_some(pat, scrut, span);
446
447 let sm = self.tcx.sess.source_map();
448 let next_token_start = sm.span_extend_while_whitespace(span.clone()).hi();
449 let line_span = sm.span_extend_to_line(span.clone()).with_lo(next_token_start);
450 let else_keyword_span = sm.span_until_whitespace(line_span);
451 self.lint_single_let(span, Some(else_keyword_span), let_else_span);
452 }
453 }
454
455 fn check_irrefutable_option_some(
457 &self,
458 pat: &'p Pat<'tcx>,
459 initializer: Option<&Expr<'tcx>>,
460 span: Span,
461 ) -> Option<LetElseReplacementSuggestion> {
462 if let sm = self.tcx.sess.source_map()
463 && let Some(initializer) = initializer
464 && let Some(s_ty) = initializer.ty.ty_adt_def()
465 && self.tcx.is_diagnostic_item(rustc_span::sym::Option, s_ty.did())
466 && let ExprKind::Scope { value, .. } = initializer.kind
467 && let initializer_expr = &self.thir[value]
468 && let ExprKind::Adt(AdtExpr { fields, .. }) = &initializer_expr.kind
469 && let Some(field) = fields.first()
470 && let inner = &self.thir[field.expr]
471 && let Some(inner_ty) = inner.ty.ty_adt_def()
472 && self.tcx.is_diagnostic_item(rustc_span::sym::Option, inner_ty.did())
473 && let Ok(rhs) = sm.span_to_snippet(inner.span)
474 && let Ok(lhs) = sm.span_to_snippet(pat.span)
475 {
476 let lhs = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Some({0})", lhs))
})format!("Some({})", lhs);
477 Some(LetElseReplacementSuggestion { span, lhs, rhs })
478 } else {
479 None
480 }
481 }
482
483 fn check_match(
484 &mut self,
485 scrut: ExprId,
486 arms: &[ArmId],
487 source: hir::MatchSource,
488 expr_span: Span,
489 ) {
490 let scrut = &self.thir[scrut];
491 let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
492
493 let mut tarms = Vec::with_capacity(arms.len());
494 for &arm in arms {
495 let arm = &self.thir.arms[arm];
496 let got_error = self.with_hir_source(arm.hir_id, |this| {
497 let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
498 let arm =
499 MatchArm { pat, arm_data: this.hir_source, has_guard: arm.guard.is_some() };
500 tarms.push(arm);
501 false
502 });
503 if got_error {
504 return;
505 }
506 }
507
508 let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
509
510 match source {
511 hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
514 hir::MatchSource::ForLoopDesugar
515 | hir::MatchSource::Postfix
516 | hir::MatchSource::Normal
517 | hir::MatchSource::FormatArgs => {
518 let is_match_arm =
519 #[allow(non_exhaustive_omitted_patterns)] match source {
hir::MatchSource::Postfix | hir::MatchSource::Normal => true,
_ => false,
}matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
520 report_arm_reachability(&cx, &report, is_match_arm);
521 }
522 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
525 }
526
527 let witnesses = report.non_exhaustiveness_witnesses;
529 if !witnesses.is_empty() {
530 if source == hir::MatchSource::ForLoopDesugar
531 && let [_, snd_arm] = *arms
532 {
533 let pat = &self.thir[snd_arm].pattern;
535 if true {
match (&pat.span.desugaring_kind(), &Some(DesugaringKind::ForLoop)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
537 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
538 let [pat_field] = &subpatterns[..] else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
539 self.check_binding_is_irrefutable(
540 &pat_field.pattern,
541 "`for` loop binding",
542 None,
543 None,
544 );
545 } else {
546 let braces_span = match source {
549 hir::MatchSource::Normal => scrut
550 .span
551 .find_ancestor_in_same_ctxt(expr_span)
552 .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
553 hir::MatchSource::Postfix => {
554 scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
557 let sm = self.tcx.sess.source_map();
558 let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
559 if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
560 let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
561 sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
563 } else {
564 None
565 }
566 })
567 }
568 hir::MatchSource::ForLoopDesugar
569 | hir::MatchSource::TryDesugar(_)
570 | hir::MatchSource::AwaitDesugar
571 | hir::MatchSource::FormatArgs => None,
572 };
573
574 let would_be_exhaustive_without_guards = {
577 let any_arm_has_guard = tarms.iter().any(|arm| arm.has_guard);
578 any_arm_has_guard && {
579 let guardless_arms: Vec<_> =
580 tarms.iter().map(|arm| MatchArm { has_guard: false, ..*arm }).collect();
581 rustc_pattern_analysis::rustc::analyze_match(&cx, &guardless_arms, scrut.ty)
582 .is_ok_and(|report| report.non_exhaustiveness_witnesses.is_empty())
583 }
584 };
585 self.error = Err(report_non_exhaustive_match(
586 &cx,
587 self.thir,
588 scrut.ty,
589 scrut.span,
590 witnesses,
591 arms,
592 braces_span,
593 would_be_exhaustive_without_guards,
594 ));
595 }
596 }
597 }
598
599 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("lint_single_let",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(599u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["let_span",
"else_keyword_span", "let_else_span"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_span)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&else_keyword_span)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_else_span)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
report_irrefutable_let_patterns(self.tcx, self.hir_source,
self.let_source, 1, let_span, else_keyword_span,
let_else_span);
}
}
}#[instrument(level = "trace", skip(self))]
600 fn lint_single_let(
601 &mut self,
602 let_span: Span,
603 else_keyword_span: Option<Span>,
604 let_else_span: Option<LetElseReplacementSuggestion>,
605 ) {
606 report_irrefutable_let_patterns(
607 self.tcx,
608 self.hir_source,
609 self.let_source,
610 1,
611 let_span,
612 else_keyword_span,
613 let_else_span,
614 );
615 }
616
617 fn analyze_binding(
618 &mut self,
619 pat: &'p Pat<'tcx>,
620 refutability: RefutableFlag,
621 scrut: Option<&Expr<'tcx>>,
622 ) -> Result<(PatCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
623 let cx = self.new_cx(refutability, None, scrut, pat.span);
624 let pat = self.lower_pattern(&cx, pat)?;
625 let arms = [MatchArm { pat, arm_data: self.hir_source, has_guard: false }];
626 let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
627 Ok((cx, report))
628 }
629
630 fn is_let_irrefutable(
631 &mut self,
632 pat: &'p Pat<'tcx>,
633 scrut: Option<&Expr<'tcx>>,
634 ) -> Result<RefutableFlag, ErrorGuaranteed> {
635 let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
636 report_arm_reachability(&cx, &report, false);
638 Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
641 }
642
643 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("check_binding_is_irrefutable",
"rustc_mir_build::thir::pattern::check_match",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
::tracing_core::__macro_support::Option::Some(643u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
::tracing_core::field::FieldSet::new(&["pat", "origin",
"scrut", "sp"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&origin as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrut)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&sp)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
let pattern_ty = pat.ty;
let Ok((cx, report)) =
self.analyze_binding(pat, Irrefutable, scrut) else { return };
let witnesses = report.non_exhaustiveness_witnesses;
if witnesses.is_empty() { return; }
let inform = sp.is_some().then_some(Inform);
let mut let_suggestion = None;
let mut misc_suggestion = None;
let mut interpreted_as_const = None;
let mut interpreted_as_const_sugg = None;
if let Some(def_id) =
is_const_pat_that_looks_like_binding(self.tcx, pat) {
let span = self.tcx.def_span(def_id);
let variable = self.tcx.item_name(def_id).to_string();
interpreted_as_const =
Some(InterpretedAsConst {
span,
variable: variable.clone(),
});
interpreted_as_const_sugg =
Some(InterpretedAsConstSugg { span: pat.span, variable });
} else if let PatKind::Constant { .. } = pat.kind &&
let Ok(snippet) =
self.tcx.sess.source_map().span_to_snippet(pat.span) {
if snippet.chars().all(|c| c.is_digit(10)) {
misc_suggestion =
Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
start_span: pat.span.shrink_to_lo(),
});
}
}
if let Some(span) = sp &&
self.tcx.sess.source_map().is_span_accessible(span) &&
interpreted_as_const.is_none() && scrut.is_some() {
let mut bindings = ::alloc::vec::Vec::new();
pat.each_binding(|name, _, _, _| bindings.push(name));
let semi_span = span.shrink_to_hi();
let start_span = span.shrink_to_lo();
let end_span = semi_span.shrink_to_lo();
let count = witnesses.len();
let_suggestion =
Some(if bindings.is_empty() {
SuggestLet::If { start_span, semi_span, count }
} else { SuggestLet::Else { end_span, count } });
};
let adt_defined_here =
report_adt_defined_here(self.tcx, pattern_ty, &witnesses,
false);
let witness_1_is_privately_uninhabited =
if let Some(witness_1) = witnesses.get(0) &&
let ty::Adt(adt, args) = witness_1.ty().kind() &&
adt.is_enum() &&
let Constructor::Variant(variant_index) = witness_1.ctor() {
let variant_inhabited =
adt.variant(*variant_index).inhabited_predicate(self.tcx,
*adt).instantiate(self.tcx, args);
variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
&&
!variant_inhabited.apply_ignore_module(self.tcx,
cx.typing_env)
} else { false };
let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
self.error =
Err(self.tcx.dcx().emit_err(PatternNotCovered {
span: pat.span,
origin,
uncovered: Uncovered::new(pat.span, &cx, witnesses),
inform,
interpreted_as_const,
interpreted_as_const_sugg,
witness_1_is_privately_uninhabited,
witness_1,
_p: (),
pattern_ty,
let_suggestion,
misc_suggestion,
adt_defined_here,
}));
}
}
}#[instrument(level = "trace", skip(self))]
644 fn check_binding_is_irrefutable(
645 &mut self,
646 pat: &'p Pat<'tcx>,
647 origin: &str,
648 scrut: Option<&Expr<'tcx>>,
649 sp: Option<Span>,
650 ) {
651 let pattern_ty = pat.ty;
652
653 let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
654 let witnesses = report.non_exhaustiveness_witnesses;
655 if witnesses.is_empty() {
656 return;
658 }
659
660 let inform = sp.is_some().then_some(Inform);
661 let mut let_suggestion = None;
662 let mut misc_suggestion = None;
663 let mut interpreted_as_const = None;
664 let mut interpreted_as_const_sugg = None;
665
666 if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, pat) {
667 let span = self.tcx.def_span(def_id);
668 let variable = self.tcx.item_name(def_id).to_string();
669 interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() });
671 interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable });
672 } else if let PatKind::Constant { .. } = pat.kind
673 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
674 {
675 if snippet.chars().all(|c| c.is_digit(10)) {
677 misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
679 start_span: pat.span.shrink_to_lo(),
680 });
681 }
682 }
683
684 if let Some(span) = sp
685 && self.tcx.sess.source_map().is_span_accessible(span)
686 && interpreted_as_const.is_none()
687 && scrut.is_some()
688 {
689 let mut bindings = vec![];
690 pat.each_binding(|name, _, _, _| bindings.push(name));
691
692 let semi_span = span.shrink_to_hi();
693 let start_span = span.shrink_to_lo();
694 let end_span = semi_span.shrink_to_lo();
695 let count = witnesses.len();
696
697 let_suggestion = Some(if bindings.is_empty() {
698 SuggestLet::If { start_span, semi_span, count }
699 } else {
700 SuggestLet::Else { end_span, count }
701 });
702 };
703
704 let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
705
706 let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
709 && let ty::Adt(adt, args) = witness_1.ty().kind()
710 && adt.is_enum()
711 && let Constructor::Variant(variant_index) = witness_1.ctor()
712 {
713 let variant_inhabited = adt
714 .variant(*variant_index)
715 .inhabited_predicate(self.tcx, *adt)
716 .instantiate(self.tcx, args);
717 variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
718 && !variant_inhabited.apply_ignore_module(self.tcx, cx.typing_env)
719 } else {
720 false
721 };
722
723 let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
724
725 self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
726 span: pat.span,
727 origin,
728 uncovered: Uncovered::new(pat.span, &cx, witnesses),
729 inform,
730 interpreted_as_const,
731 interpreted_as_const_sugg,
732 witness_1_is_privately_uninhabited,
733 witness_1,
734 _p: (),
735 pattern_ty,
736 let_suggestion,
737 misc_suggestion,
738 adt_defined_here,
739 }));
740 }
741}
742
743fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
755 let PatKind::Binding { name, mode, ty, subpattern: Some(ref sub), .. } = pat.kind else {
757 return;
758 };
759
760 let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty);
761
762 let sess = cx.tcx.sess;
763
764 let mut_outer = match mode.0 {
766 ByRef::No if is_binding_by_move(ty) => {
767 let mut conflicts_ref = Vec::new();
769 sub.each_binding(|_, mode, _, span| {
770 if #[allow(non_exhaustive_omitted_patterns)] match mode {
ByRef::Yes(..) => true,
_ => false,
}matches!(mode, ByRef::Yes(..)) {
771 conflicts_ref.push(span)
772 }
773 });
774 if !conflicts_ref.is_empty() {
775 sess.dcx().emit_err(BorrowOfMovedValue {
776 binding_span: pat.span,
777 conflicts_ref,
778 name: Ident::new(name, pat.span),
779 ty,
780 suggest_borrowing: Some(pat.span.shrink_to_lo()),
781 });
782 }
783 return;
784 }
785 ByRef::No => return,
786 ByRef::Yes(_, m) => m,
787 };
788
789 let mut conflicts_move = Vec::new();
792 let mut conflicts_mut_mut = Vec::new();
793 let mut conflicts_mut_ref = Vec::new();
794 sub.each_binding(|name, mode, ty, span| {
795 match mode {
796 ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) {
797 (Mutability::Not, Mutability::Not) => {}
799 (Mutability::Mut, Mutability::Mut) => {
801 conflicts_mut_mut.push(Conflict::Mut { span, name })
802 }
803 (Mutability::Not, Mutability::Mut) => {
804 conflicts_mut_ref.push(Conflict::Mut { span, name })
805 }
806 (Mutability::Mut, Mutability::Not) => {
807 conflicts_mut_ref.push(Conflict::Ref { span, name })
808 }
809 },
810 ByRef::No if is_binding_by_move(ty) => {
811 conflicts_move.push(Conflict::Moved { span, name }) }
813 ByRef::No => {} }
815 });
816
817 let report_mut_mut = !conflicts_mut_mut.is_empty();
818 let report_mut_ref = !conflicts_mut_ref.is_empty();
819 let report_move_conflict = !conflicts_move.is_empty();
820
821 let mut occurrences = match mut_outer {
822 Mutability::Mut => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Conflict::Mut { span: pat.span, name }]))vec![Conflict::Mut { span: pat.span, name }],
823 Mutability::Not => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Conflict::Ref { span: pat.span, name }]))vec![Conflict::Ref { span: pat.span, name }],
824 };
825 occurrences.extend(conflicts_mut_mut);
826 occurrences.extend(conflicts_mut_ref);
827 occurrences.extend(conflicts_move);
828
829 if report_mut_mut {
831 sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
833 } else if report_mut_ref {
834 match mut_outer {
836 Mutability::Mut => {
837 sess.dcx().emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
838 }
839 Mutability::Not => {
840 sess.dcx().emit_err(AlreadyBorrowed { span: pat.span, occurrences });
841 }
842 };
843 } else if report_move_conflict {
844 sess.dcx().emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
846 }
847}
848
849fn check_for_bindings_named_same_as_variants(
850 cx: &MatchVisitor<'_, '_>,
851 pat: &Pat<'_>,
852 rf: RefutableFlag,
853) {
854 if let PatKind::Binding {
855 name,
856 mode: BindingMode(ByRef::No, Mutability::Not),
857 subpattern: None,
858 ty,
859 ..
860 } = pat.kind
861 && let ty::Adt(edef, _) = ty.peel_refs().kind()
862 && edef.is_enum()
863 && edef
864 .variants()
865 .iter()
866 .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
867 {
868 let variant_count = edef.variants().len();
869 let ty_path = { let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(edef.did()) }with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
870 cx.tcx.emit_node_span_lint(
871 BINDINGS_WITH_VARIANT_NAME,
872 cx.hir_source,
873 pat.span,
874 BindingsWithVariantName {
875 suggestion: if rf == Refutable || variant_count == 1 {
879 Some(pat.span)
880 } else {
881 None
882 },
883 ty_path,
884 name: Ident::new(name, pat.span),
885 },
886 )
887 }
888}
889
890fn check_never_pattern<'tcx>(
892 cx: &PatCtxt<'_, 'tcx>,
893 pat: &Pat<'tcx>,
894) -> Result<(), ErrorGuaranteed> {
895 if let PatKind::Never = pat.kind {
896 if !cx.is_uninhabited(pat.ty) {
897 return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
898 }
899 }
900 Ok(())
901}
902
903fn report_irrefutable_let_patterns(
904 tcx: TyCtxt<'_>,
905 id: HirId,
906 source: LetSource,
907 count: usize,
908 span: Span,
909 else_keyword_span: Option<Span>,
910 let_else_span: Option<LetElseReplacementSuggestion>,
911) {
912 macro_rules! emit_diag {
913 ($lint:tt) => {{
914 tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
915 }};
916 }
917
918 match source {
919 LetSource::None | LetSource::PlainLet | LetSource::Else => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
920 LetSource::IfLet | LetSource::ElseIfLet => {
tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
IrrefutableLetPatternsIfLet { count });
}emit_diag!(IrrefutableLetPatternsIfLet),
921 LetSource::IfLetGuard => {
tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
IrrefutableLetPatternsIfLetGuard { count });
}emit_diag!(IrrefutableLetPatternsIfLetGuard),
922 LetSource::LetElse => {
923 let spans = match else_keyword_span {
924 Some(else_keyword_span) => {
925 let mut spans = MultiSpan::from_span(else_keyword_span);
926 spans.push_span_label(
927 span,
928 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("assigning to binding pattern will always succeed"))msg!("assigning to binding pattern will always succeed"),
929 );
930 spans
931 }
932 None => span.into(),
933 };
934
935 tcx.emit_node_span_lint(
936 IRREFUTABLE_LET_PATTERNS,
937 id,
938 spans,
939 IrrefutableLetPatternsLetElse { be_replaced: let_else_span },
940 );
941 }
942 LetSource::WhileLet => {
tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
IrrefutableLetPatternsWhileLet { count });
}emit_diag!(IrrefutableLetPatternsWhileLet),
943 }
944}
945
946fn report_unreachable_pattern<'p, 'tcx>(
948 cx: &PatCtxt<'p, 'tcx>,
949 hir_id: HirId,
950 pat: &DeconstructedPat<'p, 'tcx>,
951 explanation: &RedundancyExplanation<'p, 'tcx>,
952 whole_arm_span: Option<Span>,
953) {
954 static CAP_COVERED_BY_MANY: usize = 4;
955 let pat_span = pat.data().span;
956 let mut lint = UnreachablePatternInner {
957 span: Some(pat_span),
958 matches_no_values: None,
959 matches_no_values_ty: **pat.ty(),
960 uninhabited_note: None,
961 covered_by_catchall: None,
962 covered_by_one: None,
963 covered_by_many: None,
964 wanted_constant: None,
965 accessible_constant: None,
966 inaccessible_constant: None,
967 pattern_let_binding: None,
968 suggest_remove: None,
969 };
970 let mut covered_by_many_n_more_count = None;
971 match explanation.covered_by.as_slice() {
972 [] => {
973 lint.span = None; lint.uninhabited_note = Some(()); lint.matches_no_values = Some(pat_span);
977 lint.suggest_remove = whole_arm_span; pat.walk(&mut |subpat| {
979 let ty = **subpat.ty();
980 if cx.is_uninhabited(ty) {
981 lint.matches_no_values_ty = ty;
982 false } else if #[allow(non_exhaustive_omitted_patterns)] match subpat.ctor() {
Constructor::Ref | Constructor::UnionField => true,
_ => false,
}matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
984 false } else {
986 true
987 }
988 });
989 }
990 [covering_pat] if pat_is_catchall(covering_pat) => {
991 let pat = covering_pat.data();
993 lint.covered_by_catchall = Some(pat.span);
994 find_fallback_pattern_typo(cx, hir_id, pat, &mut lint);
995 }
996 [covering_pat] => {
997 lint.covered_by_one = Some(covering_pat.data().span);
998 }
999 covering_pats => {
1000 let mut iter = covering_pats.iter();
1001 let mut multispan = MultiSpan::from_span(pat_span);
1002 for p in iter.by_ref().take(CAP_COVERED_BY_MANY) {
1003 multispan.push_span_label(p.data().span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("matches some of the same values"))msg!("matches some of the same values"));
1004 }
1005 let remain = iter.count();
1006 if remain == 0 {
1007 multispan.push_span_label(pat_span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("collectively making this unreachable"))msg!("collectively making this unreachable"));
1008 } else {
1009 covered_by_many_n_more_count = Some(remain);
1010 multispan.push_span_label(
1011 pat_span,
1012 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable"))msg!("...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable"),
1013 );
1014 }
1015 lint.covered_by_many = Some(multispan);
1016 }
1017 }
1018 cx.tcx.emit_node_span_lint(
1019 UNREACHABLE_PATTERNS,
1020 hir_id,
1021 pat_span,
1022 UnreachablePattern { inner: lint, covered_by_many_n_more_count },
1023 );
1024}
1025
1026fn find_fallback_pattern_typo<'tcx>(
1028 cx: &PatCtxt<'_, 'tcx>,
1029 hir_id: HirId,
1030 pat: &Pat<'tcx>,
1031 lint: &mut UnreachablePatternInner<'_>,
1032) {
1033 if cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).is_allow() {
1034 return;
1037 }
1038 if let PatKind::Binding { name, subpattern: None, ty, .. } = pat.kind {
1039 let mut accessible = ::alloc::vec::Vec::new()vec![];
1041 let mut accessible_path = ::alloc::vec::Vec::new()vec![];
1042 let mut inaccessible = ::alloc::vec::Vec::new()vec![];
1043 let mut imported = ::alloc::vec::Vec::new()vec![];
1044 let mut imported_spans = ::alloc::vec::Vec::new()vec![];
1045 let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
1046 let parent = cx.tcx.hir_get_parent_item(hir_id);
1047
1048 for item in cx.tcx.hir_crate_items(()).free_items() {
1049 if let DefKind::Use = cx.tcx.def_kind(item.owner_id) {
1050 let item = cx.tcx.hir_expect_item(item.owner_id.def_id);
1052 let hir::ItemKind::Use(path, _) = item.kind else {
1053 continue;
1054 };
1055 if let Some(value_ns) = path.res.value_ns
1056 && let Res::Def(DefKind::Const { .. }, id) = value_ns
1057 && infcx.can_eq(
1058 param_env,
1059 ty,
1060 cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(),
1061 )
1062 {
1063 if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1064 let item_name = cx.tcx.item_name(id);
1066 accessible.push(item_name);
1067 accessible_path.push({ let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(id) }with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1068 } else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) {
1069 let ident = item.kind.ident().unwrap();
1072 imported.push(ident.name);
1073 imported_spans.push(ident.span);
1074 }
1075 }
1076 }
1077 if let DefKind::Const { .. } = cx.tcx.def_kind(item.owner_id)
1078 && infcx.can_eq(
1079 param_env,
1080 ty,
1081 cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(),
1082 )
1083 {
1084 let item_name = cx.tcx.item_name(item.owner_id);
1086 let vis = cx.tcx.visibility(item.owner_id);
1087 if vis.is_accessible_from(parent, cx.tcx) {
1088 accessible.push(item_name);
1089 let path = { let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(item.owner_id) }with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
1096 accessible_path.push(path);
1097 } else if name == item_name {
1098 inaccessible.push(cx.tcx.def_span(item.owner_id));
1101 }
1102 }
1103 }
1104 if let Some((i, &const_name)) =
1105 accessible.iter().enumerate().find(|&(_, &const_name)| const_name == name)
1106 {
1107 lint.wanted_constant = Some(WantedConstant {
1109 span: pat.span,
1110 is_typo: false,
1111 const_name: const_name.to_string(),
1112 const_path: accessible_path[i].clone(),
1113 });
1114 } else if let Some(name) = find_best_match_for_name(&accessible, name, None) {
1115 lint.wanted_constant = Some(WantedConstant {
1117 span: pat.span,
1118 is_typo: true,
1119 const_name: name.to_string(),
1120 const_path: name.to_string(),
1121 });
1122 } else if let Some(i) =
1123 imported.iter().enumerate().find(|&(_, &const_name)| const_name == name).map(|(i, _)| i)
1124 {
1125 lint.accessible_constant = Some(imported_spans[i]);
1128 } else if let Some(name) = find_best_match_for_name(&imported, name, None) {
1129 lint.wanted_constant = Some(WantedConstant {
1132 span: pat.span,
1133 is_typo: true,
1134 const_path: name.to_string(),
1135 const_name: name.to_string(),
1136 });
1137 } else if !inaccessible.is_empty() {
1138 for span in inaccessible {
1139 lint.inaccessible_constant = Some(span);
1141 }
1142 } else {
1143 for (_, node) in cx.tcx.hir_parent_iter(hir_id) {
1146 match node {
1147 hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(let_stmt), .. }) => {
1148 if let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind {
1149 if name == binding_name.name {
1150 lint.pattern_let_binding = Some(binding_name.span);
1151 }
1152 }
1153 }
1154 hir::Node::Block(hir::Block { stmts, .. }) => {
1155 for stmt in *stmts {
1156 if let hir::StmtKind::Let(let_stmt) = stmt.kind
1157 && let hir::PatKind::Binding(_, _, binding_name, _) =
1158 let_stmt.pat.kind
1159 && name == binding_name.name
1160 {
1161 lint.pattern_let_binding = Some(binding_name.span);
1162 }
1163 }
1164 }
1165 hir::Node::Item(_) => break,
1166 _ => {}
1167 }
1168 }
1169 }
1170 }
1171}
1172
1173fn report_arm_reachability<'p, 'tcx>(
1175 cx: &PatCtxt<'p, 'tcx>,
1176 report: &UsefulnessReport<'p, 'tcx>,
1177 is_match_arm: bool,
1178) {
1179 let sm = cx.tcx.sess.source_map();
1180 for (arm, is_useful) in report.arm_usefulness.iter() {
1181 if let Usefulness::Redundant(explanation) = is_useful {
1182 let hir_id = arm.arm_data;
1183 let arm_span = cx.tcx.hir_span(hir_id);
1184 let whole_arm_span = if is_match_arm {
1185 if let Some(comma) = sm.span_followed_by(arm_span, ",") {
1187 Some(arm_span.to(comma))
1188 } else {
1189 Some(arm_span)
1190 }
1191 } else {
1192 None
1193 };
1194 report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
1195 }
1196 }
1197}
1198
1199fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
1201 match pat.ctor() {
1202 Constructor::Wildcard => true,
1203 Constructor::Struct | Constructor::Ref => {
1204 pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
1205 }
1206 _ => false,
1207 }
1208}
1209
1210fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx>) -> Option<DefId> {
1216 if let Some(def_id) = try { pat.extra.as_deref()?.expanded_const? }
1220 && #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(def_id) {
DefKind::Const { .. } => true,
_ => false,
}matches!(tcx.def_kind(def_id), DefKind::Const { .. })
1221 && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span)
1222 && snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
1223 {
1224 Some(def_id)
1225 } else {
1226 None
1227 }
1228}
1229
1230fn report_non_exhaustive_match<'p, 'tcx>(
1232 cx: &PatCtxt<'p, 'tcx>,
1233 thir: &Thir<'tcx>,
1234 scrut_ty: Ty<'tcx>,
1235 sp: Span,
1236 witnesses: Vec<WitnessPat<'p, 'tcx>>,
1237 arms: &[ArmId],
1238 braces_span: Option<Span>,
1239 would_be_exhaustive_without_guards: bool,
1240) -> ErrorGuaranteed {
1241 let is_empty_match = arms.is_empty();
1242 let non_empty_enum = match scrut_ty.kind() {
1243 ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
1244 _ => false,
1245 };
1246 if is_empty_match && !non_empty_enum {
1249 return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
1250 cx,
1251 scrut_span: sp,
1252 braces_span,
1253 ty: scrut_ty,
1254 });
1255 }
1256
1257 let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
1259 let mut err = {
cx.tcx.dcx().struct_span_err(sp,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("non-exhaustive patterns: {0} not covered",
joined_patterns))
})).with_code(E0004)
}struct_span_code_err!(
1260 cx.tcx.dcx(),
1261 sp,
1262 E0004,
1263 "non-exhaustive patterns: {joined_patterns} not covered"
1264 );
1265 err.span_label(
1266 sp,
1267 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("pattern{0} {1} not covered",
if witnesses.len() == 1 { "" } else { "s" }, joined_patterns))
})format!(
1268 "pattern{} {} not covered",
1269 rustc_errors::pluralize!(witnesses.len()),
1270 joined_patterns
1271 ),
1272 );
1273
1274 if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
1276 report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
1277 {
1278 let mut multi_span = MultiSpan::from_span(adt_def_span);
1279 multi_span.push_span_label(adt_def_span, "");
1280 for Variant { span } in variants {
1281 multi_span.push_span_label(span, "not covered");
1282 }
1283 err.span_note(multi_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` defined here", ty))
})format!("`{ty}` defined here"));
1284 }
1285 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the matched value is of type `{0}`",
scrut_ty))
})format!("the matched value is of type `{}`", scrut_ty));
1286
1287 if !is_empty_match {
1288 let mut special_tys = FxIndexSet::default();
1289 collect_special_tys(cx, &witnesses[0], &mut special_tys);
1291
1292 for ty in special_tys {
1293 if ty.is_ptr_sized_integral() {
1294 if ty.inner() == cx.tcx.types.usize {
1295 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively",
ty))
})format!(
1296 "`{ty}::MAX` is not treated as exhaustive, \
1297 so half-open ranges are necessary to match exhaustively",
1298 ));
1299 } else if ty.inner() == cx.tcx.types.isize {
1300 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}::MIN` and `{0}::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively",
ty))
})format!(
1301 "`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \
1302 so half-open ranges are necessary to match exhaustively",
1303 ));
1304 }
1305 } else if ty.inner() == cx.tcx.types.str_ {
1306 err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1307 } else if cx.is_foreign_non_exhaustive_enum(ty) {
1308 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively",
ty))
})format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
1309 } else if cx.is_uninhabited(ty.inner()) {
1310 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is uninhabited but is not being matched by value, so a wildcard `_` is required",
ty))
})format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
1313 }
1314 }
1315 }
1316
1317 if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
1318 if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.typing_env) {
1319 err.note("references are always considered inhabited");
1320 }
1321 }
1322
1323 for &arm in arms {
1324 let arm = &thir.arms[arm];
1325 if let Some(def_id) = is_const_pat_that_looks_like_binding(cx.tcx, &arm.pattern) {
1326 let const_name = cx.tcx.item_name(def_id);
1327 err.span_label(
1328 arm.pattern.span,
1329 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this pattern doesn\'t introduce a new catch-all binding, but rather pattern matches against the value of constant `{0}`",
const_name))
})format!(
1330 "this pattern doesn't introduce a new catch-all binding, but rather pattern \
1331 matches against the value of constant `{const_name}`",
1332 ),
1333 );
1334 err.span_note(cx.tcx.def_span(def_id), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("constant `{0}` defined here",
const_name))
})format!("constant `{const_name}` defined here"));
1335 err.span_suggestion_verbose(
1336 arm.pattern.span.shrink_to_hi(),
1337 "if you meant to introduce a binding, use a different name",
1338 "_var".to_string(),
1339 Applicability::MaybeIncorrect,
1340 );
1341 }
1342 }
1343
1344 let suggest_the_witnesses = witnesses.len() < 4;
1346 let suggested_arm = if suggest_the_witnesses {
1347 let pattern = witnesses
1348 .iter()
1349 .map(|witness| cx.print_witness_pat(witness))
1350 .collect::<Vec<String>>()
1351 .join(" | ");
1352 if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() {
1353 pattern
1355 } else {
1356 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} => todo!()", pattern))
})format!("{pattern} => todo!()")
1357 }
1358 } else {
1359 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("_ => todo!()"))
})format!("_ => todo!()")
1360 };
1361 let mut suggestion = None;
1362 let sm = cx.tcx.sess.source_map();
1363 match arms {
1364 [] if let Some(braces_span) = braces_span => {
1365 let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
1367 (::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}", snippet))
})format!("\n{snippet}"), " ")
1368 } else {
1369 (" ".to_string(), "")
1370 };
1371 suggestion = Some((
1372 braces_span,
1373 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {{{0}{1}{2},{0}}}", indentation,
more, suggested_arm))
})format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
1374 ));
1375 }
1376 [only] => {
1377 let only = &thir[*only];
1378 let (pre_indentation, is_multiline) = if let Some(snippet) =
1379 sm.indentation_before(only.span)
1380 && let Ok(with_trailing) =
1381 sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1382 && sm.is_multiline(with_trailing)
1383 {
1384 (::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}", snippet))
})format!("\n{snippet}"), true)
1385 } else {
1386 (" ".to_string(), false)
1387 };
1388 let only_body = &thir[only.body];
1389 let comma = if #[allow(non_exhaustive_omitted_patterns)] match only_body.kind {
ExprKind::Block { .. } => true,
_ => false,
}matches!(only_body.kind, ExprKind::Block { .. })
1390 && only.span.eq_ctxt(only_body.span)
1391 && is_multiline
1392 {
1393 ""
1394 } else {
1395 ","
1396 };
1397 suggestion = Some((
1398 only.span.shrink_to_hi(),
1399 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", comma, pre_indentation,
suggested_arm))
})format!("{comma}{pre_indentation}{suggested_arm}"),
1400 ));
1401 }
1402 [.., prev, last] => {
1403 let prev = &thir[*prev];
1404 let last = &thir[*last];
1405 if prev.span.eq_ctxt(last.span) {
1406 let last_body = &thir[last.body];
1407 let comma = if #[allow(non_exhaustive_omitted_patterns)] match last_body.kind {
ExprKind::Block { .. } => true,
_ => false,
}matches!(last_body.kind, ExprKind::Block { .. })
1408 && last.span.eq_ctxt(last_body.span)
1409 {
1410 ""
1411 } else {
1412 ","
1413 };
1414 let spacing = if sm.is_multiline(prev.span.between(last.span)) {
1415 sm.indentation_before(last.span).map(|indent| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}", indent))
})format!("\n{indent}"))
1416 } else {
1417 Some(" ".to_string())
1418 };
1419 if let Some(spacing) = spacing {
1420 suggestion = Some((
1421 last.span.shrink_to_hi(),
1422 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", comma, spacing,
suggested_arm))
})format!("{comma}{spacing}{suggested_arm}"),
1423 ));
1424 }
1425 }
1426 }
1427 _ => {}
1428 }
1429
1430 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("ensure that all possible cases are being handled by adding a match arm with a wildcard pattern{0}{1}",
if witnesses.len() > 1 && suggest_the_witnesses &&
suggestion.is_some() {
", a match arm with multiple or-patterns"
} else { "" },
match witnesses.len() {
0 if suggestion.is_some() => " as shown",
0 => "",
1 if suggestion.is_some() =>
" or an explicit pattern as shown",
1 => " or an explicit pattern",
_ if suggestion.is_some() =>
" as shown, or multiple match arms",
_ => " or multiple match arms",
}))
})format!(
1431 "ensure that all possible cases are being handled by adding a match arm with a wildcard \
1432 pattern{}{}",
1433 if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
1434 ", a match arm with multiple or-patterns"
1435 } else {
1436 ""
1438 },
1439 match witnesses.len() {
1440 0 if suggestion.is_some() => " as shown",
1442 0 => "",
1443 1 if suggestion.is_some() => " or an explicit pattern as shown",
1444 1 => " or an explicit pattern",
1445 _ if suggestion.is_some() => " as shown, or multiple match arms",
1446 _ => " or multiple match arms",
1447 },
1448 );
1449
1450 if would_be_exhaustive_without_guards {
1451 err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
1452 }
1453 if let Some((span, sugg)) = suggestion {
1454 err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
1455 } else {
1456 err.help(msg);
1457 }
1458 err.emit()
1459}
1460
1461fn joined_uncovered_patterns<'p, 'tcx>(
1462 cx: &PatCtxt<'p, 'tcx>,
1463 witnesses: &[WitnessPat<'p, 'tcx>],
1464) -> String {
1465 const LIMIT: usize = 3;
1466 let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.print_witness_pat(pat);
1467 match witnesses {
1468 [] => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
1469 [witness] => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`",
cx.print_witness_pat(witness)))
})format!("`{}`", cx.print_witness_pat(witness)),
1470 [head @ .., tail] if head.len() < LIMIT => {
1471 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1472 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` and `{1}`",
head.join("`, `"), cx.print_witness_pat(tail)))
})format!("`{}` and `{}`", head.join("`, `"), cx.print_witness_pat(tail))
1473 }
1474 _ => {
1475 let (head, tail) = witnesses.split_at(LIMIT);
1476 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1477 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` and {1} more",
head.join("`, `"), tail.len()))
})format!("`{}` and {} more", head.join("`, `"), tail.len())
1478 }
1479 }
1480}
1481
1482fn collect_special_tys<'tcx>(
1484 cx: &PatCtxt<'_, 'tcx>,
1485 pat: &WitnessPat<'_, 'tcx>,
1486 special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
1487) {
1488 if #[allow(non_exhaustive_omitted_patterns)] match pat.ctor() {
Constructor::NonExhaustive | Constructor::Never => true,
_ => false,
}matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
1489 special_tys.insert(*pat.ty());
1490 }
1491 if let Constructor::IntRange(range) = pat.ctor() {
1492 if cx.is_range_beyond_boundaries(range, *pat.ty()) {
1493 special_tys.insert(*pat.ty());
1495 }
1496 }
1497 pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
1498}
1499
1500fn report_adt_defined_here<'tcx>(
1501 tcx: TyCtxt<'tcx>,
1502 ty: Ty<'tcx>,
1503 witnesses: &[WitnessPat<'_, 'tcx>],
1504 point_at_non_local_ty: bool,
1505) -> Option<AdtDefinedHere<'tcx>> {
1506 let ty = ty.peel_refs();
1507 let ty::Adt(def, _) = ty.kind() else {
1508 return None;
1509 };
1510 let adt_def_span =
1511 tcx.hir_get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
1512 let adt_def_span = if point_at_non_local_ty {
1513 adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
1514 } else {
1515 adt_def_span?
1516 };
1517
1518 let mut variants = ::alloc::vec::Vec::new()vec![];
1519 for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
1520 variants.push(Variant { span });
1521 }
1522 Some(AdtDefinedHere { adt_def_span, ty, variants })
1523}
1524
1525fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
1526 tcx: TyCtxt<'tcx>,
1527 def: AdtDef<'tcx>,
1528 patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
1529) -> Vec<Span> {
1530 let mut covered = ::alloc::vec::Vec::new()vec![];
1531 for pattern in patterns {
1532 if let Constructor::Variant(variant_index) = pattern.ctor() {
1533 if let ty::Adt(this_def, _) = pattern.ty().kind()
1534 && this_def.did() != def.did()
1535 {
1536 continue;
1537 }
1538 let sp = def.variant(*variant_index).ident(tcx).span;
1539 if covered.contains(&sp) {
1540 continue;
1543 }
1544 covered.push(sp);
1545 }
1546 covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1547 }
1548 covered
1549}