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