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