Auto merge of #135465 - jhpratt:rollup-7p93bct, r=jhpratt
Rollup of 10 pull requests Successful merges: - #134498 (Fix cycle error only occurring with -Zdump-mir) - #134977 (Detect `mut arg: &Ty` meant to be `arg: &mut Ty` and provide structured suggestion) - #135390 (Re-added regression test for #122638) - #135393 (uefi: helpers: Introduce OwnedDevicePath) - #135440 (rm unnecessary `OpaqueTypeDecl` wrapper) - #135441 (Make sure to mark `IMPL_TRAIT_REDUNDANT_CAPTURES` as `Allow` in edition 2024) - #135444 (Update books) - #135450 (Fix emscripten-wasm-eh with unwind=abort) - #135452 (bootstrap: fix outdated feature name in comment) - #135454 (llvm: Allow sized-word rather than ymmword in tests) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
35c2908177
34 changed files with 492 additions and 83 deletions
|
@ -25,8 +25,8 @@ pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
|
|||
let opaque_types = infcx
|
||||
.take_opaque_types()
|
||||
.into_iter()
|
||||
.map(|(opaque_type_key, decl)| {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
|
||||
.map(|(opaque_type_key, hidden_type)| {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(hidden_type);
|
||||
register_member_constraints(
|
||||
typeck,
|
||||
&mut member_constraints,
|
||||
|
|
|
@ -2451,10 +2451,10 @@ fn add_order_independent_options(
|
|||
}
|
||||
|
||||
if sess.target.os == "emscripten" {
|
||||
cmd.cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
|
||||
"-sDISABLE_EXCEPTION_CATCHING=1"
|
||||
} else if sess.opts.unstable_opts.emscripten_wasm_eh {
|
||||
cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh {
|
||||
"-fwasm-exceptions"
|
||||
} else if sess.panic_strategy() == PanicStrategy::Abort {
|
||||
"-sDISABLE_EXCEPTION_CATCHING=1"
|
||||
} else {
|
||||
"-sDISABLE_EXCEPTION_CATCHING=0"
|
||||
});
|
||||
|
|
|
@ -436,9 +436,9 @@ fn check_opaque_meets_bounds<'tcx>(
|
|||
} else {
|
||||
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
|
||||
for (mut key, mut ty) in infcx.take_opaque_types() {
|
||||
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
|
||||
ty.ty = infcx.resolve_vars_if_possible(ty.ty);
|
||||
key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
|
||||
sanity_check_found_hidden_type(tcx, key, ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1873,7 +1873,7 @@ pub(super) fn check_coroutine_obligations(
|
|||
// Check that any hidden types found when checking these stalled coroutine obligations
|
||||
// are valid.
|
||||
for (key, ty) in infcx.take_opaque_types() {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type);
|
||||
let hidden_type = infcx.resolve_vars_if_possible(ty);
|
||||
let key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, hidden_type)?;
|
||||
}
|
||||
|
|
|
@ -85,6 +85,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
self.annotate_expected_due_to_let_ty(err, expr, error);
|
||||
self.annotate_loop_expected_due_to_inference(err, expr, error);
|
||||
if self.annotate_mut_binding_to_immutable_binding(err, expr, error) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(#73154): For now, we do leak check when coercing function
|
||||
// pointers in typeck, instead of only during borrowck. This can lead
|
||||
|
@ -795,6 +798,98 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Detect the following case
|
||||
///
|
||||
/// ```text
|
||||
/// fn change_object(mut a: &Ty) {
|
||||
/// let a = Ty::new();
|
||||
/// b = a;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where the user likely meant to modify the value behind there reference, use `a` as an out
|
||||
/// parameter, instead of mutating the local binding. When encountering this we suggest:
|
||||
///
|
||||
/// ```text
|
||||
/// fn change_object(a: &'_ mut Ty) {
|
||||
/// let a = Ty::new();
|
||||
/// *b = a;
|
||||
/// }
|
||||
/// ```
|
||||
fn annotate_mut_binding_to_immutable_binding(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
error: Option<TypeError<'tcx>>,
|
||||
) -> bool {
|
||||
if let Some(TypeError::Sorts(ExpectedFound { expected, found })) = error
|
||||
&& let ty::Ref(_, inner, hir::Mutability::Not) = expected.kind()
|
||||
|
||||
// The difference between the expected and found values is one level of borrowing.
|
||||
&& self.can_eq(self.param_env, *inner, found)
|
||||
|
||||
// We have an `ident = expr;` assignment.
|
||||
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. }) =
|
||||
self.tcx.parent_hir_node(expr.hir_id)
|
||||
&& rhs.hir_id == expr.hir_id
|
||||
|
||||
// We are assigning to some binding.
|
||||
&& let hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
hir::Path { res: hir::def::Res::Local(hir_id), .. },
|
||||
)) = lhs.kind
|
||||
&& let hir::Node::Pat(pat) = self.tcx.hir_node(*hir_id)
|
||||
|
||||
// The pattern we have is an fn argument.
|
||||
&& let hir::Node::Param(hir::Param { ty_span, .. }) =
|
||||
self.tcx.parent_hir_node(pat.hir_id)
|
||||
&& let item = self.tcx.hir().get_parent_item(pat.hir_id)
|
||||
&& let item = self.tcx.hir_owner_node(item)
|
||||
&& let Some(fn_decl) = item.fn_decl()
|
||||
|
||||
// We have a mutable binding in the argument.
|
||||
&& let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind
|
||||
|
||||
// Look for the type corresponding to the argument pattern we have in the argument list.
|
||||
&& let Some(ty_sugg) = fn_decl
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|ty| {
|
||||
if ty.span == *ty_span
|
||||
&& let hir::TyKind::Ref(lt, x) = ty.kind
|
||||
{
|
||||
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
|
||||
Some((
|
||||
x.ty.span.shrink_to_lo(),
|
||||
format!(
|
||||
"{}mut ",
|
||||
if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " }
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
{
|
||||
let sugg = vec![
|
||||
ty_sugg,
|
||||
(pat.span.until(ident.span), String::new()),
|
||||
(lhs.span.shrink_to_lo(), "*".to_string()),
|
||||
];
|
||||
// We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the
|
||||
// assignment from `ident = val;` to `*ident = val;`.
|
||||
err.multipart_suggestion_verbose(
|
||||
"you might have meant to mutate the pointed at value being passed in, instead of \
|
||||
changing the reference in the local binding",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn annotate_alternative_method_deref(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
|
|
|
@ -562,9 +562,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
// types or by using this function at the end of writeback and running it as a
|
||||
// fixpoint.
|
||||
let opaque_types = self.fcx.infcx.clone_opaque_types();
|
||||
for (opaque_type_key, decl) in opaque_types {
|
||||
let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
|
||||
let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
|
||||
for (opaque_type_key, hidden_type) in opaque_types {
|
||||
let hidden_type = self.resolve(hidden_type, &hidden_type.span);
|
||||
let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span);
|
||||
|
||||
if !self.fcx.next_trait_solver() {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
|
||||
|
|
|
@ -155,12 +155,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
.opaque_type_storage
|
||||
.opaque_types
|
||||
.iter()
|
||||
.map(|(k, v)| (*k, v.hidden_type.ty))
|
||||
.map(|(k, v)| (*k, v.ty))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn take_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
|
||||
self.take_opaque_types().into_iter().map(|(k, v)| (k, v.hidden_type.ty)).collect()
|
||||
self.take_opaque_types().into_iter().map(|(k, v)| (k, v.ty)).collect()
|
||||
}
|
||||
|
||||
/// Given the (canonicalized) result to a canonical query,
|
||||
|
|
|
@ -234,7 +234,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
|||
pub fn iter_opaque_types(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (ty::OpaqueTypeKey<'tcx>, ty::OpaqueHiddenType<'tcx>)> + '_ {
|
||||
self.opaque_type_storage.opaque_types.iter().map(|(&k, v)| (k, v.hidden_type))
|
||||
self.opaque_type_storage.opaque_types.iter().map(|(&k, &v)| (k, v))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,20 +19,9 @@ use crate::traits::{self, Obligation, PredicateObligations};
|
|||
|
||||
mod table;
|
||||
|
||||
pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>;
|
||||
pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>;
|
||||
pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
|
||||
|
||||
/// Information about the opaque types whose values we
|
||||
/// are inferring in this function (these are the `impl Trait` that
|
||||
/// appear in the return type).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpaqueTypeDecl<'tcx> {
|
||||
/// The hidden types that have been inferred for this opaque type.
|
||||
/// There can be multiple, but they are all `lub`ed together at the end
|
||||
/// to obtain the canonical hidden type.
|
||||
pub hidden_type: OpaqueHiddenType<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// This is a backwards compatibility hack to prevent breaking changes from
|
||||
/// lazy TAIT around RPIT handling.
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc_middle::bug;
|
|||
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::{OpaqueTypeDecl, OpaqueTypeMap};
|
||||
use super::OpaqueTypeMap;
|
||||
use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
|
@ -11,15 +11,19 @@ pub(crate) struct OpaqueTypeStorage<'tcx> {
|
|||
/// Opaque types found in explicit return types and their
|
||||
/// associated fresh inference variable. Writeback resolves these
|
||||
/// variables to get the concrete type, which can be used to
|
||||
/// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
|
||||
/// 'de-opaque' OpaqueHiddenType, after typeck is done with all functions.
|
||||
pub opaque_types: OpaqueTypeMap<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> OpaqueTypeStorage<'tcx> {
|
||||
#[instrument(level = "debug")]
|
||||
pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>, idx: Option<OpaqueHiddenType<'tcx>>) {
|
||||
if let Some(idx) = idx {
|
||||
self.opaque_types.get_mut(&key).unwrap().hidden_type = idx;
|
||||
pub(crate) fn remove(
|
||||
&mut self,
|
||||
key: OpaqueTypeKey<'tcx>,
|
||||
prev: Option<OpaqueHiddenType<'tcx>>,
|
||||
) {
|
||||
if let Some(prev) = prev {
|
||||
*self.opaque_types.get_mut(&key).unwrap() = prev;
|
||||
} else {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
match self.opaque_types.swap_remove(&key) {
|
||||
|
@ -59,13 +63,12 @@ impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> {
|
|||
key: OpaqueTypeKey<'tcx>,
|
||||
hidden_type: OpaqueHiddenType<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if let Some(decl) = self.storage.opaque_types.get_mut(&key) {
|
||||
let prev = std::mem::replace(&mut decl.hidden_type, hidden_type);
|
||||
if let Some(entry) = self.storage.opaque_types.get_mut(&key) {
|
||||
let prev = std::mem::replace(entry, hidden_type);
|
||||
self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev)));
|
||||
return Some(prev.ty);
|
||||
}
|
||||
let decl = OpaqueTypeDecl { hidden_type };
|
||||
self.storage.opaque_types.insert(key, decl);
|
||||
self.storage.opaque_types.insert(key, hidden_type);
|
||||
self.undo_log.push(UndoLog::OpaqueTypes(key, None));
|
||||
None
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ declare_lint! {
|
|||
/// To fix this, remove the `use<'a>`, since the lifetime is already captured
|
||||
/// since it is in scope.
|
||||
pub IMPL_TRAIT_REDUNDANT_CAPTURES,
|
||||
Warn,
|
||||
Allow,
|
||||
"redundant precise-capturing `use<...>` syntax on an `impl Trait`",
|
||||
}
|
||||
|
||||
|
|
|
@ -1555,16 +1555,22 @@ pub fn write_allocations<'tcx>(
|
|||
write!(w, " (vtable: impl {dyn_ty} for {ty})")?
|
||||
}
|
||||
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
|
||||
match tcx.eval_static_initializer(did) {
|
||||
Ok(alloc) => {
|
||||
write!(w, " (static: {}, ", tcx.def_path_str(did))?;
|
||||
write_allocation_track_relocs(w, alloc)?;
|
||||
write!(w, " (static: {}", tcx.def_path_str(did))?;
|
||||
if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)
|
||||
&& tcx.hir().body_const_context(body.source.def_id()).is_some()
|
||||
{
|
||||
// Statics may be cyclic and evaluating them too early
|
||||
// in the MIR pipeline may cause cycle errors even though
|
||||
// normal compilation is fine.
|
||||
write!(w, ")")?;
|
||||
} else {
|
||||
match tcx.eval_static_initializer(did) {
|
||||
Ok(alloc) => {
|
||||
write!(w, ", ")?;
|
||||
write_allocation_track_relocs(w, alloc)?;
|
||||
}
|
||||
Err(_) => write!(w, ", error during initializer evaluation)")?,
|
||||
}
|
||||
Err(_) => write!(
|
||||
w,
|
||||
" (static: {}, error during initializer evaluation)",
|
||||
tcx.def_path_str(did)
|
||||
)?,
|
||||
}
|
||||
}
|
||||
Some(GlobalAlloc::Static(did)) => {
|
||||
|
|
|
@ -799,6 +799,9 @@ passes_unused_assign = value assigned to `{$name}` is never read
|
|||
passes_unused_assign_passed = value passed to `{$name}` is never read
|
||||
.help = maybe it is overwritten before being read?
|
||||
|
||||
passes_unused_assign_suggestion =
|
||||
you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding
|
||||
|
||||
passes_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read
|
||||
.help = did you mean to capture by reference instead?
|
||||
|
||||
|
|
|
@ -1787,9 +1787,26 @@ pub(crate) struct IneffectiveUnstableImpl;
|
|||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_unused_assign)]
|
||||
#[help]
|
||||
pub(crate) struct UnusedAssign {
|
||||
pub name: String,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<UnusedAssignSuggestion>,
|
||||
#[help]
|
||||
pub help: bool,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(passes_unused_assign_suggestion, applicability = "maybe-incorrect")]
|
||||
pub(crate) struct UnusedAssignSuggestion {
|
||||
pub pre: &'static str,
|
||||
#[suggestion_part(code = "{pre}mut ")]
|
||||
pub ty_span: Span,
|
||||
#[suggestion_part(code = "")]
|
||||
pub ty_ref_span: Span,
|
||||
#[suggestion_part(code = "*")]
|
||||
pub ident_span: Span,
|
||||
#[suggestion_part(code = "")]
|
||||
pub expr_ref_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
|
@ -1360,7 +1360,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
|||
fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) {
|
||||
self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| {
|
||||
if local.init.is_some() {
|
||||
self.warn_about_dead_assign(spans, hir_id, ln, var);
|
||||
self.warn_about_dead_assign(spans, hir_id, ln, var, None);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1460,7 +1460,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
// as being used.
|
||||
let ln = self.live_node(expr.hir_id, expr.span);
|
||||
let var = self.variable(var_hid, expr.span);
|
||||
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
|
||||
let sugg = self.annotate_mut_binding_to_immutable_binding(var_hid, expr);
|
||||
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var, sugg);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -1585,6 +1586,70 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Detect the following case
|
||||
///
|
||||
/// ```text
|
||||
/// fn change_object(mut a: &Ty) {
|
||||
/// let a = Ty::new();
|
||||
/// b = &a;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where the user likely meant to modify the value behind there reference, use `a` as an out
|
||||
/// parameter, instead of mutating the local binding. When encountering this we suggest:
|
||||
///
|
||||
/// ```text
|
||||
/// fn change_object(a: &'_ mut Ty) {
|
||||
/// let a = Ty::new();
|
||||
/// *b = a;
|
||||
/// }
|
||||
/// ```
|
||||
fn annotate_mut_binding_to_immutable_binding(
|
||||
&self,
|
||||
var_hid: HirId,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<errors::UnusedAssignSuggestion> {
|
||||
if let hir::Node::Expr(parent) = self.ir.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::Assign(_, rhs, _) = parent.kind
|
||||
&& let hir::ExprKind::AddrOf(borrow_kind, _mut, inner) = rhs.kind
|
||||
&& let hir::BorrowKind::Ref = borrow_kind
|
||||
&& let hir::Node::Pat(pat) = self.ir.tcx.hir_node(var_hid)
|
||||
&& let hir::Node::Param(hir::Param { ty_span, .. }) =
|
||||
self.ir.tcx.parent_hir_node(pat.hir_id)
|
||||
&& let item_id = self.ir.tcx.hir().get_parent_item(pat.hir_id)
|
||||
&& let item = self.ir.tcx.hir_owner_node(item_id)
|
||||
&& let Some(fn_decl) = item.fn_decl()
|
||||
&& let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind
|
||||
&& let Some((ty_span, pre)) = fn_decl
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|ty| {
|
||||
if ty.span == *ty_span
|
||||
&& let hir::TyKind::Ref(lt, mut_ty) = ty.kind
|
||||
{
|
||||
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
|
||||
Some((
|
||||
mut_ty.ty.span.shrink_to_lo(),
|
||||
if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " },
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
{
|
||||
Some(errors::UnusedAssignSuggestion {
|
||||
ty_span,
|
||||
pre,
|
||||
ty_ref_span: pat.span.until(ident.span),
|
||||
ident_span: expr.span.shrink_to_lo(),
|
||||
expr_ref_span: rhs.span.until(inner.span),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "INFO")]
|
||||
fn report_unused(
|
||||
&self,
|
||||
|
@ -1738,15 +1803,23 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
suggs
|
||||
}
|
||||
|
||||
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
|
||||
fn warn_about_dead_assign(
|
||||
&self,
|
||||
spans: Vec<Span>,
|
||||
hir_id: HirId,
|
||||
ln: LiveNode,
|
||||
var: Variable,
|
||||
suggestion: Option<errors::UnusedAssignSuggestion>,
|
||||
) {
|
||||
if !self.live_on_exit(ln, var)
|
||||
&& let Some(name) = self.should_warn(var)
|
||||
{
|
||||
let help = suggestion.is_none();
|
||||
self.ir.tcx.emit_node_span_lint(
|
||||
lint::builtin::UNUSED_ASSIGNMENTS,
|
||||
hir_id,
|
||||
spans,
|
||||
errors::UnusedAssign { name },
|
||||
errors::UnusedAssign { name, suggestion, help },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue