Auto merge of #104417 - mejrs:mir_build, r=davidtwco

Migrate rustc_mir_build diagnostics

Rebases https://github.com/rust-lang/rust/pull/100854

~~The remaining issue is how to better resolve 72bea68af4~~

~~The diagnostic macros seems to generate a broken diagnostic, and I couldn't figure out how to manually format the fluent message, so I hardcoded the format string for now. I'd like pointers to a better fix for this.~~

Also, I'm not 100% sure I didn't mess up a rebase somewhere 🙂

r? `@davidtwco`
This commit is contained in:
bors 2022-12-18 08:53:49 +00:00
commit 35a99eef32
12 changed files with 1210 additions and 309 deletions

View File

@ -4069,6 +4069,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",

View File

@ -0,0 +1,301 @@
mir_build_unconditional_recursion = function cannot return without recursing
.label = cannot return without recursing
.help = a `loop` may express intention better if this is on purpose
mir_build_unconditional_recursion_call_site_label = recursive call site
mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe =
call to unsafe function `{$function}` is unsafe and requires unsafe block (error E0133)
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless =
call to unsafe function is unsafe and requires unsafe block (error E0133)
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
use of inline assembly is unsafe and requires unsafe block (error E0133)
.note = inline assembly is entirely unchecked and can cause undefined behavior
.label = use of inline assembly
mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe
block (error E0133)
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
.label = initializing type with `rustc_layout_scalar_valid_range` attr
mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe =
use of mutable static is unsafe and requires unsafe block (error E0133)
.note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
.label = use of mutable static
mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe =
use of extern static is unsafe and requires unsafe block (error E0133)
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
.label = use of extern static
mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe =
dereference of raw pointer is unsafe and requires unsafe block (error E0133)
.note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
.label = dereference of raw pointer
mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe =
access to union field is unsafe and requires unsafe block (error E0133)
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
.label = access to union field
mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe =
mutation of layout constrained field is unsafe and requires unsafe block (error E0133)
.note = mutating layout constrained fields cannot statically be checked for valid values
.label = mutation of layout constrained field
mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe =
borrow of layout constrained field with interior mutability is unsafe and requires unsafe block (error E0133)
.note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
.label = borrow of layout constrained field with interior mutability
mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe =
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
.note = can only be called if the required target features are available
.label = call to function with `#[target_feature]`
mir_build_call_to_unsafe_fn_requires_unsafe =
call to unsafe function `{$function}` is unsafe and requires unsafe block
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_call_to_unsafe_fn_requires_unsafe_nameless =
call to unsafe function is unsafe and requires unsafe block
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
call to unsafe function `{$function}` is unsafe and requires unsafe function or block
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed =
call to unsafe function is unsafe and requires unsafe function or block
.note = consult the function's documentation for information on how to avoid undefined behavior
.label = call to unsafe function
mir_build_inline_assembly_requires_unsafe =
use of inline assembly is unsafe and requires unsafe block
.note = inline assembly is entirely unchecked and can cause undefined behavior
.label = use of inline assembly
mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
use of inline assembly is unsafe and requires unsafe function or block
.note = inline assembly is entirely unchecked and can cause undefined behavior
.label = use of inline assembly
mir_build_initializing_type_with_requires_unsafe =
initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
.label = initializing type with `rustc_layout_scalar_valid_range` attr
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
.label = initializing type with `rustc_layout_scalar_valid_range` attr
mir_build_mutable_static_requires_unsafe =
use of mutable static is unsafe and requires unsafe block
.note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
.label = use of mutable static
mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
use of mutable static is unsafe and requires unsafe function or block
.note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
.label = use of mutable static
mir_build_extern_static_requires_unsafe =
use of extern static is unsafe and requires unsafe block
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
.label = use of extern static
mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
use of extern static is unsafe and requires unsafe function or block
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
.label = use of extern static
mir_build_deref_raw_pointer_requires_unsafe =
dereference of raw pointer is unsafe and requires unsafe block
.note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
.label = dereference of raw pointer
mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
dereference of raw pointer is unsafe and requires unsafe function or block
.note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
.label = dereference of raw pointer
mir_build_union_field_requires_unsafe =
access to union field is unsafe and requires unsafe block
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
.label = access to union field
mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
access to union field is unsafe and requires unsafe function or block
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
.label = access to union field
mir_build_mutation_of_layout_constrained_field_requires_unsafe =
mutation of layout constrained field is unsafe and requires unsafe block
.note = mutating layout constrained fields cannot statically be checked for valid values
.label = mutation of layout constrained field
mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
mutation of layout constrained field is unsafe and requires unsafe function or block
.note = mutating layout constrained fields cannot statically be checked for valid values
.label = mutation of layout constrained field
mir_build_borrow_of_layout_constrained_field_requires_unsafe =
borrow of layout constrained field with interior mutability is unsafe and requires unsafe block
.note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
.label = borrow of layout constrained field with interior mutability
mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block
.note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
.label = borrow of layout constrained field with interior mutability
mir_build_call_to_fn_with_requires_unsafe =
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
.note = can only be called if the required target features are available
.label = call to function with `#[target_feature]`
mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block
.note = can only be called if the required target features are available
.label = call to function with `#[target_feature]`
mir_build_unused_unsafe = unnecessary `unsafe` block
.label = unnecessary `unsafe` block
mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
.def_note = `{$peeled_ty}` defined here
.type_note = the matched value is of type `{$ty}`
.non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive
.reference_note = references are always considered inhabited
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
mir_build_static_in_pattern = statics cannot be referenced in patterns
mir_build_assoc_const_in_pattern = associated consts cannot be referenced in patterns
mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns
mir_build_non_const_path = runtime values cannot be referenced in patterns
mir_build_unreachable_pattern = unreachable pattern
.label = unreachable pattern
.catchall_label = matches any value
mir_build_const_pattern_depends_on_generic_parameter =
constant pattern depends on a generic parameter
mir_build_could_not_eval_const_pattern = could not evaluate constant pattern
mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
lower range bound must be less than or equal to upper
.label = lower bound larger than upper bound
.teach_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.
mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper
mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count ->
[one] pattern
*[other] patterns
} in let chain
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match
.help = consider moving {$count ->
[one] it
*[other] them
} outside of the construct
mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count ->
[one] pattern
*[other] patterns
} in let chain
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match
.help = consider moving {$count ->
[one] it
*[other] them
} into the body
mir_build_bindings_with_variant_name =
pattern binding `{$ident}` is named the same as one of the variants of the type `{$ty_path}`
.suggestion = to match on the variant, qualify the path
mir_build_irrefutable_let_patterns_generic_let = irrefutable `let` {$count ->
[one] pattern
*[other] patterns
}
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match, so the `let` is useless
.help = consider removing `let`
mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count ->
[one] pattern
*[other] patterns
}
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match, so the `if let` is useless
.help = consider replacing the `if let` with a `let`
mir_build_irrefutable_let_patterns_if_let_guard = irrefutable `if let` guard {$count ->
[one] pattern
*[other] patterns
}
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match, so the guard is useless
.help = consider removing the guard and adding a `let` inside the match arm
mir_build_irrefutable_let_patterns_let_else = irrefutable `let...else` {$count ->
[one] pattern
*[other] patterns
}
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match, so the `else` clause is useless
.help = consider removing the `else` clause
mir_build_irrefutable_let_patterns_while_let = irrefutable `while let` {$count ->
[one] pattern
*[other] patterns
}
.note = {$count ->
[one] this pattern
*[other] these patterns
} will always match, so the loop will never exit
.help = consider instead using a `loop {"{"} ... {"}"}` with a `let` inside it
mir_build_borrow_of_moved_value = borrow of moved value
.label = value moved into `{$name}` here
.occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
.value_borrowed_label = value borrowed here after move
.suggestion = borrow this binding in the pattern to avoid moving the value
mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once at a time
.label = first mutable borrow, by `{$name}`, occurs here
.mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
.immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
.moved = also moved into `{$name_moved}` here

View File

@ -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",

View File

@ -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" }

View File

@ -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,79 +518,187 @@ enum UnsafeOpKind {
use UnsafeOpKind::*;
impl UnsafeOpKind {
pub fn simple_description(&self) -> &'static str {
pub fn emit_unsafe_op_in_unsafe_fn_lint(
&self,
tcx: TyCtxt<'_>,
hir_id: hir::HirId,
span: Span,
) {
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]`",
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()),
},
),
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 => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
),
UseOfMutableStatic => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
),
UseOfExternStatic => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
),
DerefOfRawPointer => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
),
AccessToUnionField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
),
MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
),
BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
),
CallToFunctionWith(did) => tcx.emit_spanned_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
span,
function: &tcx.def_path_str(*did),
},
),
}
}
pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
pub fn emit_requires_unsafe_err(
&self,
tcx: TyCtxt<'_>,
span: Span,
unsafe_op_in_unsafe_fn_allowed: bool,
) {
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())
},
"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",
),
InitializingTypeWith => (
Cow::Borrowed(self.simple_description()),
"initializing a layout restricted type's field with a value outside the valid \
range is undefined behavior",
),
UseOfMutableStatic => (
Cow::Borrowed(self.simple_description()),
"mutable statics can be mutated by multiple threads: aliasing violations or data \
races will cause undefined behavior",
),
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",
),
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",
),
AccessToUnionField => (
Cow::Borrowed(self.simple_description()),
"the field may not be properly initialized: using uninitialized data will cause \
undefined behavior",
),
MutationOfLayoutConstrainedField => (
Cow::Borrowed(self.simple_description()),
"mutating layout constrained fields cannot statically be checked for valid values",
),
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",
),
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",
),
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),
});
}
}
}
}

View File

@ -0,0 +1,616 @@
use crate::thir::pattern::MatchCheckCtxt;
use rustc_errors::Handler;
use rustc_errors::{
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, Ty};
use rustc_span::{symbol::Ident, Span};
#[derive(LintDiagnostic)]
#[diag(mir_build_unconditional_recursion)]
#[help]
pub struct UnconditionalRecursion {
#[label]
pub span: Span,
#[label(mir_build_unconditional_recursion_call_site_label)]
pub call_sites: Vec<Span>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)]
#[note]
pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)]
pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)]
#[note]
pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(Diagnostic)]
#[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = "E0133")]
#[note]
pub struct CallToUnsafeFunctionRequiresUnsafe<'a> {
#[primary_span]
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(Diagnostic)]
#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = "E0133")]
#[note]
pub struct CallToUnsafeFunctionRequiresUnsafeNameless {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
#[primary_span]
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(Diagnostic)]
#[diag(
mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed,
code = "E0133"
)]
#[note]
pub struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_inline_assembly_requires_unsafe, code = "E0133")]
#[note]
pub struct UseOfInlineAssemblyRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_initializing_type_with_requires_unsafe, code = "E0133")]
#[note]
pub struct InitializingTypeWithRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
code = "E0133"
)]
#[note]
pub struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_mutable_static_requires_unsafe, code = "E0133")]
#[note]
pub struct UseOfMutableStaticRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_extern_static_requires_unsafe, code = "E0133")]
#[note]
pub struct UseOfExternStaticRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_deref_raw_pointer_requires_unsafe, code = "E0133")]
#[note]
pub struct DerefOfRawPointerRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_union_field_requires_unsafe, code = "E0133")]
#[note]
pub struct AccessToUnionFieldRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = "E0133")]
#[note]
pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
code = "E0133"
)]
#[note]
pub struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = "E0133")]
#[note]
pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
code = "E0133"
)]
#[note]
pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")]
#[note]
pub struct CallToFunctionWithRequiresUnsafe<'a> {
#[primary_span]
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(Diagnostic)]
#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
#[note]
pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
#[primary_span]
#[label]
pub span: Span,
pub function: &'a str,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unused_unsafe)]
pub struct UnusedUnsafe {
#[label]
pub span: Span,
#[subdiagnostic]
pub enclosing: Option<UnusedUnsafeEnclosing>,
}
#[derive(Subdiagnostic)]
pub enum UnusedUnsafeEnclosing {
#[label(mir_build_unused_unsafe_enclosing_block_label)]
Block {
#[primary_span]
span: Span,
},
#[label(mir_build_unused_unsafe_enclosing_fn_label)]
Function {
#[primary_span]
span: Span,
},
}
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
pub expr_span: Span,
pub span: Span,
pub ty: Ty<'tcx>,
}
impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut diag = handler.struct_span_err_with_code(
self.span,
rustc_errors::fluent::mir_build_non_exhaustive_patterns_type_not_empty,
error_code!(E0004),
);
let peeled_ty = self.ty.peel_refs();
diag.set_arg("ty", self.ty);
diag.set_arg("peeled_ty", peeled_ty);
if let ty::Adt(def, _) = peeled_ty.kind() {
let def_span = self
.cx
.tcx
.hir()
.get_if_local(def.did())
.and_then(|node| node.ident())
.map(|ident| ident.span)
.unwrap_or_else(|| self.cx.tcx.def_span(def.did()));
// workaround to make test pass
let mut span: MultiSpan = def_span.into();
span.push_span_label(def_span, "");
diag.span_note(span, rustc_errors::fluent::def_note);
}
let is_variant_list_non_exhaustive = match self.ty.kind() {
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => {
true
}
_ => false,
};
if is_variant_list_non_exhaustive {
diag.note(rustc_errors::fluent::non_exhaustive_type_note);
} else {
diag.note(rustc_errors::fluent::type_note);
}
if let ty::Ref(_, sub_ty, _) = self.ty.kind() {
if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.param_env) {
diag.note(rustc_errors::fluent::reference_note);
}
}
let mut suggestion = None;
let sm = self.cx.tcx.sess.source_map();
if self.span.eq_ctxt(self.expr_span) {
// Get the span for the empty match body `{}`.
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
(format!("\n{}", snippet), " ")
} else {
(" ".to_string(), "")
};
suggestion = Some((
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
format!(
" {{{indentation}{more}_ => todo!(),{indentation}}}",
indentation = indentation,
more = more,
),
));
}
if let Some((span, sugg)) = suggestion {
diag.span_suggestion_verbose(
span,
rustc_errors::fluent::suggestion,
sugg,
Applicability::HasPlaceholders,
);
} else {
diag.help(rustc_errors::fluent::help);
}
diag
}
}
#[derive(Diagnostic)]
#[diag(mir_build_static_in_pattern, code = "E0158")]
pub struct StaticInPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_assoc_const_in_pattern, code = "E0158")]
pub struct AssocConstInPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_param_in_pattern, code = "E0158")]
pub struct ConstParamInPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_non_const_path, code = "E0080")]
pub struct NonConstPath {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unreachable_pattern)]
pub struct UnreachablePattern {
#[label]
pub span: Option<Span>,
#[label(catchall_label)]
pub catchall: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_pattern_depends_on_generic_parameter)]
pub struct ConstPatternDependsOnGenericParameter {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_could_not_eval_const_pattern)]
pub struct CouldNotEvalConstPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = "E0030")]
pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper {
#[primary_span]
#[label]
pub span: Span,
#[note(teach_note)]
pub teach: Option<()>,
}
#[derive(Diagnostic)]
#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")]
pub struct LowerRangeBoundMustBeLessThanUpper {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_leading_irrefutable_let_patterns)]
#[note]
#[help]
pub struct LeadingIrrefutableLetPatterns {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_trailing_irrefutable_let_patterns)]
#[note]
#[help]
pub struct TrailingIrrefutableLetPatterns {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_bindings_with_variant_name, code = "E0170")]
pub struct BindingsWithVariantName {
#[suggestion(code = "{ty_path}::{ident}", applicability = "machine-applicable")]
pub suggestion: Option<Span>,
pub ty_path: String,
pub ident: Ident,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_irrefutable_let_patterns_generic_let)]
#[note]
#[help]
pub struct IrrefutableLetPatternsGenericLet {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_irrefutable_let_patterns_if_let)]
#[note]
#[help]
pub struct IrrefutableLetPatternsIfLet {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_irrefutable_let_patterns_if_let_guard)]
#[note]
#[help]
pub struct IrrefutableLetPatternsIfLetGuard {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_irrefutable_let_patterns_let_else)]
#[note]
#[help]
pub struct IrrefutableLetPatternsLetElse {
pub count: usize,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_irrefutable_let_patterns_while_let)]
#[note]
#[help]
pub struct IrrefutableLetPatternsWhileLet {
pub count: usize,
}
#[derive(Diagnostic)]
#[diag(mir_build_borrow_of_moved_value)]
pub struct BorrowOfMovedValue<'tcx> {
#[primary_span]
pub span: Span,
#[label]
#[label(occurs_because_label)]
pub binding_span: Span,
#[label(value_borrowed_label)]
pub conflicts_ref: Vec<Span>,
pub name: Ident,
pub ty: Ty<'tcx>,
#[suggestion(code = "ref ", applicability = "machine-applicable")]
pub suggest_borrowing: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(mir_build_multiple_mut_borrows)]
pub struct MultipleMutBorrows {
#[primary_span]
pub span: Span,
#[label]
pub binding_span: Span,
#[subdiagnostic]
pub occurences: Vec<MultipleMutBorrowOccurence>,
pub name: Ident,
}
#[derive(Subdiagnostic)]
pub enum MultipleMutBorrowOccurence {
#[label(mutable_borrow)]
Mutable {
#[primary_span]
span: Span,
name_mut: Ident,
},
#[label(immutable_borrow)]
Immutable {
#[primary_span]
span: Span,
name_immut: Ident,
},
#[label(moved)]
Moved {
#[primary_span]
span: Span,
name_moved: Ident,
},
}

View File

@ -19,6 +19,7 @@ extern crate rustc_middle;
mod build;
mod check_unsafety;
mod errors;
mod lints;
pub mod thir;

View File

@ -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 },
);
}
}

View File

@ -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
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 {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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