Skip to content

Migrate rustc_mir_build diagnostics #104417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Dec 18, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -4070,6 +4070,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
301 changes: 301 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/mir_build.ftl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions compiler/rustc_error_messages/src/lib.rs
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ fluent_messages! {
lint => "../locales/en-US/lint.ftl",
metadata => "../locales/en-US/metadata.ftl",
middle => "../locales/en-US/middle.ftl",
mir_build => "../locales/en-US/mir_build.ftl",
mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
monomorphize => "../locales/en-US/monomorphize.ftl",
parse => "../locales/en-US/parse.ftl",
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/Cargo.toml
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" }
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
280 changes: 185 additions & 95 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::build::ExprCategory;
use crate::errors::*;
use rustc_middle::thir::visit::{self, Visitor};

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::mir::BorrowKind;
use rustc_middle::thir::*;
@@ -12,7 +12,6 @@ use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

use std::borrow::Cow;
use std::ops::Bound;

struct UnsafetyVisitor<'a, 'tcx> {
@@ -46,7 +45,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
self.warn_unused_unsafe(
hir_id,
block_span,
Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
Some(UnusedUnsafeEnclosing::Block {
span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
}),
);
f(self);
} else {
@@ -60,7 +61,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
hir_id,
span,
if self.unsafe_op_in_unsafe_fn_allowed() {
self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
self.body_unsafety
.unsafe_fn_sig_span()
.map(|span| UnusedUnsafeEnclosing::Function { span })
} else {
None
},
@@ -83,30 +86,11 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
}
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
SafetyContext::UnsafeFn => {
let (description, note) = kind.description_and_note(self.tcx);
// unsafe_op_in_unsafe_fn is disallowed
self.tcx.struct_span_lint_hir(
UNSAFE_OP_IN_UNSAFE_FN,
self.hir_context,
span,
format!("{} is unsafe and requires unsafe block (error E0133)", description,),
|lint| lint.span_label(span, kind.simple_description()).note(note),
)
kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
}
SafetyContext::Safe => {
let (description, note) = kind.description_and_note(self.tcx);
let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
struct_span_err!(
self.tcx.sess,
span,
E0133,
"{} is unsafe and requires unsafe{} block",
description,
fn_sugg,
)
.span_label(span, kind.simple_description())
.note(note)
.emit();
kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
}
}
}
@@ -115,17 +99,15 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
&self,
hir_id: hir::HirId,
block_span: Span,
enclosing_unsafe: Option<(Span, &'static str)>,
enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
) {
let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
let msg = "unnecessary `unsafe` block";
self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| {
lint.span_label(block_span, msg);
if let Some((span, kind)) = enclosing_unsafe {
lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
}
lint
});
self.tcx.emit_spanned_lint(
UNUSED_UNSAFE,
hir_id,
block_span,
UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
);
}

/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
@@ -536,81 +518,189 @@ enum UnsafeOpKind {
use UnsafeOpKind::*;

impl UnsafeOpKind {
pub fn simple_description(&self) -> &'static str {
match self {
CallToUnsafeFunction(..) => "call to unsafe function",
UseOfInlineAssembly => "use of inline assembly",
InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
UseOfMutableStatic => "use of mutable static",
UseOfExternStatic => "use of extern static",
DerefOfRawPointer => "dereference of raw pointer",
AccessToUnionField => "access to union field",
MutationOfLayoutConstrainedField => "mutation of layout constrained field",
BorrowOfLayoutConstrainedField => {
"borrow of layout constrained field with interior mutability"
}
CallToFunctionWith(..) => "call to function with `#[target_feature]`",
}
}

pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
pub fn emit_unsafe_op_in_unsafe_fn_lint(
&self,
tcx: TyCtxt<'_>,
hir_id: hir::HirId,
span: Span,
) {
match self {
CallToUnsafeFunction(did) => (
if let Some(did) = did {
Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
} else {
Cow::Borrowed(self.simple_description())
CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
span,
function: &tcx.def_path_str(did.unwrap()),
},
"consult the function's documentation for information on how to avoid undefined \
behavior",
),
UseOfInlineAssembly => (
Cow::Borrowed(self.simple_description()),
"inline assembly is entirely unchecked and can cause undefined behavior",
CallToUnsafeFunction(..) => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
),
UseOfInlineAssembly => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
),
InitializingTypeWith => (
Cow::Borrowed(self.simple_description()),
"initializing a layout restricted type's field with a value outside the valid \
range is undefined behavior",
InitializingTypeWith => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
),
UseOfMutableStatic => (
Cow::Borrowed(self.simple_description()),
"mutable statics can be mutated by multiple threads: aliasing violations or data \
races will cause undefined behavior",
UseOfMutableStatic => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
),
UseOfExternStatic => (
Cow::Borrowed(self.simple_description()),
"extern statics are not controlled by the Rust type system: invalid data, \
aliasing violations or data races will cause undefined behavior",
UseOfExternStatic => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
),
DerefOfRawPointer => (
Cow::Borrowed(self.simple_description()),
"raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
and cause data races: all of these are undefined behavior",
DerefOfRawPointer => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
),
AccessToUnionField => (
Cow::Borrowed(self.simple_description()),
"the field may not be properly initialized: using uninitialized data will cause \
undefined behavior",
AccessToUnionField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
),
MutationOfLayoutConstrainedField => (
Cow::Borrowed(self.simple_description()),
"mutating layout constrained fields cannot statically be checked for valid values",
MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
),
BorrowOfLayoutConstrainedField => (
Cow::Borrowed(self.simple_description()),
"references to fields of layout constrained fields lose the constraints. Coupled \
with interior mutability, the field can be changed to invalid values",
BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
),
CallToFunctionWith(did) => (
Cow::from(format!(
"call to function `{}` with `#[target_feature]`",
tcx.def_path_str(*did)
)),
"can only be called if the required target features are available",
CallToFunctionWith(did) => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
span,
function: &tcx.def_path_str(*did),
},
),
}
}

pub fn emit_requires_unsafe_err(
&self,
tcx: TyCtxt<'_>,
span: Span,
unsafe_op_in_unsafe_fn_allowed: bool,
) {
match self {
CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => {
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
function: &tcx.def_path_str(did.unwrap()),
});
}
CallToUnsafeFunction(did) if did.is_some() => {
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
span,
function: &tcx.def_path_str(did.unwrap()),
});
}
CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess.emit_err(
CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
);
}
CallToUnsafeFunction(..) => {
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
}
UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
UseOfInlineAssembly => {
tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
}
InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
InitializingTypeWith => {
tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
}
UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
UseOfMutableStatic => {
tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
}
UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
UseOfExternStatic => {
tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
}
DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
DerefOfRawPointer => {
tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
}
AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess
.emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
}
AccessToUnionField => {
tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
}
MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess.emit_err(
MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
},
);
}
MutationOfLayoutConstrainedField => {
tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
}
BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess.emit_err(
BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
);
}
BorrowOfLayoutConstrainedField => {
tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
}
CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
function: &tcx.def_path_str(*did),
});
}
CallToFunctionWith(did) => {
tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
span,
function: &tcx.def_path_str(*did),
});
}
}
}
}

pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
616 changes: 616 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/lib.rs
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ extern crate rustc_middle;

mod build;
mod check_unsafety;
mod errors;
mod lints;
pub mod thir;

13 changes: 3 additions & 10 deletions compiler/rustc_mir_build/src/lints.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::errors::UnconditionalRecursion;
use rustc_data_structures::graph::iterate::{
NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
};
@@ -36,19 +37,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {

let sp = tcx.def_span(def_id);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
UNCONDITIONAL_RECURSION,
hir_id,
sp,
"function cannot return without recursing",
|lint| {
lint.span_label(sp, "cannot return without recursing");
// offer some help to the programmer.
for call_span in vis.reachable_recursive_calls {
lint.span_label(call_span, "recursive call site");
}
lint.help("a `loop` may express intention better if this is on purpose")
},
UnconditionalRecursion { span: sp, call_sites: vis.reachable_recursive_calls },
);
}
}
230 changes: 72 additions & 158 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
@@ -4,18 +4,22 @@ use super::usefulness::{
};
use super::{PatCtxt, PatternError};

use crate::errors::*;

use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed, MultiSpan,
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::*;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, Pat};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};

use rustc_session::lint::builtin::{
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
};
@@ -107,28 +111,20 @@ impl PatCtxt<'_, '_> {
for error in &self.errors {
match *error {
PatternError::StaticInPattern(span) => {
self.span_e0158(span, "statics cannot be referenced in patterns")
self.tcx.sess.emit_err(StaticInPattern { span });
}
PatternError::AssocConstInPattern(span) => {
self.span_e0158(span, "associated consts cannot be referenced in patterns")
self.tcx.sess.emit_err(AssocConstInPattern { span });
}
PatternError::ConstParamInPattern(span) => {
self.span_e0158(span, "const parameters cannot be referenced in patterns")
self.tcx.sess.emit_err(ConstParamInPattern { span });
}
PatternError::NonConstPath(span) => {
rustc_middle::mir::interpret::struct_error(
self.tcx.at(span),
"runtime values cannot be referenced in patterns",
)
.emit();
self.tcx.sess.emit_err(NonConstPath { span });
}
}
}
}

fn span_e0158(&self, span: Span, text: &str) {
struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
}
}

impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
@@ -345,29 +341,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
);
return true;
}
let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| {
let span_start = affix[0].unwrap().0;
let span_end = affix.last().unwrap().unwrap().0;
let span = span_start.to(span_end);
let cnt = affix.len();
let s = pluralize!(cnt);
cx.tcx.struct_span_lint_hir(
IRREFUTABLE_LET_PATTERNS,
top,
span,
format!("{kind} irrefutable pattern{s} in let chain"),
|lint| {
lint.note(format!(
"{these} pattern{s} will always match",
these = pluralize!("this", cnt),
))
.help(format!(
"consider moving {} {suggestion}",
if cnt > 1 { "them" } else { "it" }
))
},
);
};
if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
// The chain has a non-zero prefix of irrefutable `let` statements.

@@ -381,13 +354,21 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
// Emit the lint
let prefix = &chain_refutabilities[..until];
lint_affix(prefix, "leading", "outside of the construct");
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();
cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count });
}
}
if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
// The chain has a non-empty suffix of irrefutable `let` statements
let suffix = &chain_refutabilities[from + 1..];
lint_affix(suffix, "trailing", "into the body");
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();
cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count });
}
true
}
@@ -568,32 +549,22 @@ fn check_for_bindings_named_same_as_variants(
})
{
let variant_count = edef.variants().len();
cx.tcx.struct_span_lint_hir(
let ty_path = with_no_trimmed_paths!({
cx.tcx.def_path_str(edef.did())
});
cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
DelayDm(|| format!(
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
ident, cx.tcx.def_path_str(edef.did())
)),
|lint| {
let ty_path = cx.tcx.def_path_str(edef.did());
lint.code(error_code!(E0170));

BindingsWithVariantName {
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
if rf == Refutable || variant_count == 1 {
lint.span_suggestion(
p.span,
"to match on the variant, qualify the path",
format!("{}::{}", ty_path, ident),
Applicability::MachineApplicable,
);
}

lint
suggestion: if rf == Refutable || variant_count == 1 {
Some(p.span)
} else { None },
ty_path,
ident,
},
)
}
@@ -611,14 +582,12 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
}

fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| {
if let Some(catchall) = catchall {
// We had a catchall pattern, hint at that.
lint.span_label(span, "unreachable pattern");
lint.span_label(catchall, "matches any value");
}
lint
});
tcx.emit_spanned_lint(
UNREACHABLE_PATTERNS,
id,
span,
UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
);
}

fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
@@ -634,67 +603,18 @@ fn irrefutable_let_patterns(
span: Span,
) {
macro_rules! emit_diag {
(
$lint:expr,
$source_name:expr,
$note_sufix:expr,
$help_sufix:expr
) => {{
let s = pluralize!(count);
let these = pluralize!("this", count);
tcx.struct_span_lint_hir(
IRREFUTABLE_LET_PATTERNS,
id,
span,
format!("irrefutable {} pattern{s}", $source_name),
|lint| {
lint.note(&format!(
"{these} pattern{s} will always match, so the {}",
$note_sufix
))
.help(concat!("consider ", $help_sufix))
},
)
($lint:tt) => {{
tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
}};
}

match source {
LetSource::GenericLet => {
emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
}
LetSource::IfLet => {
emit_diag!(
lint,
"`if let`",
"`if let` is useless",
"replacing the `if let` with a `let`"
);
}
LetSource::IfLetGuard => {
emit_diag!(
lint,
"`if let` guard",
"guard is useless",
"removing the guard and adding a `let` inside the match arm"
);
}
LetSource::LetElse => {
emit_diag!(
lint,
"`let...else`",
"`else` clause is useless",
"removing the `else` clause"
);
}
LetSource::WhileLet => {
emit_diag!(
lint,
"`while let`",
"loop will never exit",
"instead using a `loop { ... }` with a `let` inside it"
);
}
};
LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet),
LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
}
}

fn is_let_irrefutable<'p, 'tcx>(
@@ -760,15 +680,17 @@ fn non_exhaustive_match<'p, 'tcx>(
// informative.
let mut err;
let pattern;
let mut patterns_len = 0;
let patterns_len;
if is_empty_match && !non_empty_enum {
err = create_e0004(
cx.tcx.sess,
sp,
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
);
pattern = "_".to_string();
cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
cx,
expr_span,
span: sp,
ty: scrut_ty,
});
return;
} else {
// FIXME: migration of this diagnostic will require list support
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can try using the list support added in #104047, maybe better as a follow-up so we can land this though.

let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
@@ -1039,24 +961,17 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
}
});
if !conflicts_ref.is_empty() {
let occurs_because = format!(
"move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
sess.emit_err(BorrowOfMovedValue {
span: pat.span,
binding_span,
conflicts_ref,
name,
typeck_results.node_type(pat.hir_id),
);
let mut err = sess.struct_span_err(pat.span, "borrow of moved value");
err.span_label(binding_span, format!("value moved into `{}` here", name))
.span_label(binding_span, occurs_because)
.span_labels(conflicts_ref, "value borrowed here after move");
if pat.span.contains(binding_span) {
err.span_suggestion_verbose(
binding_span.shrink_to_lo(),
"borrow this binding in the pattern to avoid moving the value",
"ref ".to_string(),
Applicability::MachineApplicable,
);
}
err.emit();
ty: typeck_results.node_type(pat.hir_id),
suggest_borrowing: pat
.span
.contains(binding_span)
.then(|| binding_span.shrink_to_lo()),
});
}
return;
}
@@ -1086,19 +1001,18 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
// Report errors if any.
if !conflicts_mut_mut.is_empty() {
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
let mut err = sess
.struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
for (span, name) in conflicts_mut_mut {
err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
let mut occurences = vec![];

for (span, name_mut) in conflicts_mut_mut {
occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut });
}
for (span, name) in conflicts_mut_ref {
err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
for (span, name_immut) in conflicts_mut_ref {
occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut });
}
for (span, name) in conflicts_move {
err.span_label(span, format!("also moved into `{}` here", name));
for (span, name_moved) in conflicts_move {
occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved });
}
err.emit();
sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name });
} else if !conflicts_mut_ref.is_empty() {
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
let (primary, also) = match mut_outer {
39 changes: 11 additions & 28 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
@@ -6,10 +6,12 @@ mod deconstruct_pat;
mod usefulness;

pub(crate) use self::check_match::check_match;
pub(crate) use self::usefulness::MatchCheckCtxt;

use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers;

use rustc_errors::struct_span_err;
use rustc_errors::error_code;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
@@ -139,13 +141,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
// `x..y` where `x >= y`. The range is empty => error.
(RangeEnd::Excluded, _) => {
struct_span_err!(
self.tcx.sess,
span,
E0579,
"lower range bound must be less than upper"
)
.emit();
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
PatKind::Wild
}
// `x..=y` where `x == y`.
@@ -156,23 +152,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
// `x..=y` where `x > y` hence the range is empty => error.
(RangeEnd::Included, _) => {
let mut err = struct_span_err!(
self.tcx.sess,
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
span,
E0030,
"lower range bound must be less than or equal to upper"
);
err.span_label(span, "lower bound larger than upper bound");
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"When matching against a range, the compiler \
verifies that the range is non-empty. Range \
patterns include both end-points, so this is \
equivalent to requiring the start of the range \
to be less than or equal to the end of the range.",
);
}
err.emit();
teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None },
});
PatKind::Wild
}
}
@@ -501,7 +484,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}

Err(_) => {
self.tcx.sess.span_err(span, "could not evaluate constant pattern");
self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
return pat_from_kind(PatKind::Wild);
}
};
@@ -548,11 +531,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
Err(ErrorHandled::TooGeneric) => {
// While `Reported | Linted` cases will have diagnostics emitted already
// it is not true for TooGeneric case, so we need to give user more information.
self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
pat_from_kind(PatKind::Wild)
}
Err(_) => {
self.tcx.sess.span_err(span, "could not evaluate constant pattern");
self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
pat_from_kind(PatKind::Wild)
}
}
@@ -584,7 +567,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
mir::ConstantKind::Unevaluated(..) => {
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
return PatKind::Wild;
}
}
10 changes: 5 additions & 5 deletions src/test/ui/lint/lint-uppercase-variables.rs
Original file line number Diff line number Diff line change
@@ -20,19 +20,19 @@ fn main() {

match foo::Foo::Foo {
Foo => {}
//~^ ERROR variable `Foo` should have a snake case name
//~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
//~^^^ WARN unused variable: `Foo`
//~^ ERROR variable `Foo` should have a snake case name
//~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
}

let Foo = foo::Foo::Foo;
//~^ ERROR variable `Foo` should have a snake case name
//~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
//~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`

fn in_param(Foo: foo::Foo) {}
//~^ ERROR variable `Foo` should have a snake case name
//~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
//~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`

test(1);
12 changes: 6 additions & 6 deletions src/test/ui/lint/lint-uppercase-variables.stderr
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
|
LL | Foo => {}
| ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
|
= note: `#[warn(bindings_with_variant_name)]` on by default

warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:28:9
|
LL | let Foo = foo::Foo::Foo;
| ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`

warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:33:17
|
LL | fn in_param(Foo: foo::Foo) {}
| ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`

warning: unused variable: `Foo`
--> $DIR/lint-uppercase-variables.rs:22:9