|
| 1 | +use crate::thir::pattern::MatchCheckCtxt; |
| 2 | +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; |
1 | 3 | use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
|
| 4 | +use rustc_middle::ty::{self, Ty}; |
| 5 | +use rustc_session::{parse::ParseSess, SessionDiagnostic}; |
2 | 6 | use rustc_span::Span;
|
3 | 7 |
|
4 | 8 | #[derive(LintDiagnostic)]
|
@@ -336,3 +340,92 @@ pub enum UnusedUnsafeEnclosing {
|
336 | 340 | span: Span,
|
337 | 341 | },
|
338 | 342 | }
|
| 343 | + |
| 344 | +pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { |
| 345 | + pub cx: &'m MatchCheckCtxt<'p, 'tcx>, |
| 346 | + pub expr_span: Span, |
| 347 | + pub span: Span, |
| 348 | + pub ty: Ty<'tcx>, |
| 349 | +} |
| 350 | + |
| 351 | +impl<'a> SessionDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { |
| 352 | + fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'_, ErrorGuaranteed> { |
| 353 | + let mut diag = sess.span_diagnostic.struct_span_err_with_code( |
| 354 | + self.span, |
| 355 | + rustc_errors::fluent::mir_build::non_exhaustive_patterns_type_not_empty, |
| 356 | + error_code!(E0004), |
| 357 | + ); |
| 358 | + |
| 359 | + let peeled_ty = self.ty.peel_refs(); |
| 360 | + diag.set_arg("ty", self.ty); |
| 361 | + diag.set_arg("peeled_ty", peeled_ty); |
| 362 | + |
| 363 | + if let ty::Adt(def, _) = peeled_ty.kind() { |
| 364 | + let def_span = self |
| 365 | + .cx |
| 366 | + .tcx |
| 367 | + .hir() |
| 368 | + .get_if_local(def.did()) |
| 369 | + .and_then(|node| node.ident()) |
| 370 | + .map(|ident| ident.span) |
| 371 | + .unwrap_or_else(|| self.cx.tcx.def_span(def.did())); |
| 372 | + |
| 373 | + // workaround to make test pass |
| 374 | + let mut span: MultiSpan = def_span.into(); |
| 375 | + span.push_span_label(def_span, ""); |
| 376 | + |
| 377 | + diag.span_note(span, rustc_errors::fluent::mir_build::def_note); |
| 378 | + } |
| 379 | + |
| 380 | + let is_variant_list_non_exhaustive = match self.ty.kind() { |
| 381 | + ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => { |
| 382 | + true |
| 383 | + } |
| 384 | + _ => false, |
| 385 | + }; |
| 386 | + |
| 387 | + if is_variant_list_non_exhaustive { |
| 388 | + diag.note(rustc_errors::fluent::mir_build::non_exhaustive_type_note); |
| 389 | + } else { |
| 390 | + diag.note(rustc_errors::fluent::mir_build::type_note); |
| 391 | + } |
| 392 | + |
| 393 | + if let ty::Ref(_, sub_ty, _) = self.ty.kind() { |
| 394 | + if self.cx.tcx.is_ty_uninhabited_from(self.cx.module, *sub_ty, self.cx.param_env) { |
| 395 | + diag.note(rustc_errors::fluent::mir_build::reference_note); |
| 396 | + } |
| 397 | + } |
| 398 | + |
| 399 | + let mut suggestion = None; |
| 400 | + let sm = self.cx.tcx.sess.source_map(); |
| 401 | + if self.span.eq_ctxt(self.expr_span) { |
| 402 | + // Get the span for the empty match body `{}`. |
| 403 | + let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { |
| 404 | + (format!("\n{}", snippet), " ") |
| 405 | + } else { |
| 406 | + (" ".to_string(), "") |
| 407 | + }; |
| 408 | + suggestion = Some(( |
| 409 | + self.span.shrink_to_hi().with_hi(self.expr_span.hi()), |
| 410 | + format!( |
| 411 | + " {{{indentation}{more}_ => todo!(),{indentation}}}", |
| 412 | + indentation = indentation, |
| 413 | + more = more, |
| 414 | + ), |
| 415 | + )); |
| 416 | + } |
| 417 | + |
| 418 | + if let Some((span, sugg)) = suggestion { |
| 419 | + diag.span_suggestion_verbose( |
| 420 | + span, |
| 421 | + rustc_errors::fluent::mir_build::suggestion, |
| 422 | + sugg, |
| 423 | + Applicability::HasPlaceholders, |
| 424 | + ); |
| 425 | + } else { |
| 426 | + diag.help(rustc_errors::fluent::mir_build::help); |
| 427 | + } |
| 428 | + |
| 429 | + diag |
| 430 | + } |
| 431 | +} |
0 commit comments