Auto merge of #134992 - Zalathar:rollup-pldy5w6, r=Zalathar

Rollup of 6 pull requests

Successful merges:

 - #131439 (Remove allowing static_mut_refs lint)
 - #133292 (E0277: suggest dereferencing function arguments in more cases)
 - #134877 (add suggestion for wrongly ordered format parameters)
 - #134945 (Some small nits to the borrowck suggestions for mutating a map through index)
 - #134950 (bootstrap: Overhaul and simplify the `tool_check_step!` macro)
 - #134979 (Provide structured suggestion for `impl Default` of type where all fields have defaults)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-01-01 08:10:15 +00:00
commit bc3e3015b6
24 changed files with 482 additions and 229 deletions

View file

@ -575,7 +575,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// ---------- place
self.err.multipart_suggestions(
format!(
"to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
"use `.insert()` to insert a value into a `{}`, `.get_mut()` \
to modify it, or the entry API for more flexibility",
self.ty,
),
vec![
@ -592,16 +593,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
(rv.span.shrink_to_hi(), ")".to_string()),
],
vec![
// val.get_mut(index).map(|v| { *v = rv; });
// if let Some(v) = val.get_mut(index) { *v = rv; }
(val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
".get_mut(".to_string(),
),
(
index.span.shrink_to_hi().with_hi(place.span.hi()),
").map(|val| { *val".to_string(),
") { *val".to_string(),
),
(rv.span.shrink_to_hi(), "; })".to_string()),
(rv.span.shrink_to_hi(), "; }".to_string()),
],
vec![
// let x = val.entry(index).or_insert(rv);
@ -622,21 +624,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.suggested = true;
} else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
&& let hir::ExprKind::Index(val, index, _) = receiver.kind
&& expr.span == self.assign_span
&& receiver.span == self.assign_span
{
// val[index].path(args..);
self.err.multipart_suggestion(
format!("to modify a `{}` use `.get_mut()`", self.ty),
vec![
(val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
".get_mut(".to_string(),
),
(
index.span.shrink_to_hi().with_hi(receiver.span.hi()),
").map(|val| val".to_string(),
") { val".to_string(),
),
(sp.shrink_to_hi(), ")".to_string()),
(sp.shrink_to_hi(), "; }".to_string()),
],
Applicability::MachineApplicable,
);

View file

@ -197,6 +197,8 @@ builtin_macros_format_redundant_args = redundant {$n ->
builtin_macros_format_remove_raw_ident = remove the `r#`
builtin_macros_format_reorder_format_parameter = did you mean `{$replacement}`?
builtin_macros_format_requires_string = requires at least a format string argument
builtin_macros_format_string_invalid = invalid format string: {$desc}

View file

@ -618,6 +618,17 @@ pub(crate) enum InvalidFormatStringSuggestion {
#[primary_span]
span: Span,
},
#[suggestion(
builtin_macros_format_reorder_format_parameter,
code = "{replacement}",
style = "verbose",
applicability = "machine-applicable"
)]
ReorderFormatParameter {
#[primary_span]
span: Span,
replacement: String,
},
}
#[derive(Diagnostic)]

View file

@ -321,6 +321,13 @@ fn make_format_args(
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span })
}
}
parse::Suggestion::ReorderFormatParameter(span, replacement) => {
let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end));
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::ReorderFormatParameter {
span,
replacement,
});
}
}
let guar = ecx.dcx().emit_err(e);
return ExpandResult::Ready(Err(guar));

View file

@ -2,7 +2,7 @@
//! Primarily used to extract a backtrace from stack overflow
use std::alloc::{Layout, alloc};
use std::{fmt, mem, ptr};
use std::{fmt, mem, ptr, slice};
use rustc_interface::util::{DEFAULT_STACK_SIZE, STACK_SIZE};
@ -35,20 +35,22 @@ macro raw_errln($tokens:tt) {
}
/// Signal handler installed for SIGSEGV
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#[allow(static_mut_refs)]
extern "C" fn print_stack_trace(_: libc::c_int) {
///
/// # Safety
///
/// Caller must ensure that this function is not re-entered.
unsafe extern "C" fn print_stack_trace(_: libc::c_int) {
const MAX_FRAMES: usize = 256;
// Reserve data segment so we don't have to malloc in a signal handler, which might fail
// in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking
static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES];
let stack = unsafe {
// Reserve data segment so we don't have to malloc in a signal handler, which might fail
// in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking
static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES];
// Collect return addresses
let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
let depth = libc::backtrace(&raw mut STACK_TRACE as _, MAX_FRAMES as i32);
if depth == 0 {
return;
}
&STACK_TRACE.as_slice()[0..(depth as _)]
slice::from_raw_parts(&raw const STACK_TRACE as _, depth as _)
};
// Just a stack trace is cryptic. Explain what we're doing.

View file

@ -1,9 +1,11 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Diag;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::Symbol;
use rustc_span::def_id::DefId;
use rustc_span::symbol::sym;
use crate::{LateContext, LateLintPass};
@ -149,13 +151,16 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
let hir_id = cx.tcx.local_def_id_to_hir_id(local);
let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
mk_lint(diag, orig_fields, fields);
mk_lint(cx.tcx, diag, type_def_id, parent, orig_fields, fields);
});
}
}
fn mk_lint(
tcx: TyCtxt<'_>,
diag: &mut Diag<'_, ()>,
type_def_id: DefId,
impl_def_id: DefId,
orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
fields: &[hir::ExprField<'_>],
) {
@ -175,11 +180,24 @@ fn mk_lint(
}
}
diag.help(if removed_all_fields {
"to avoid divergence in behavior between `Struct { .. }` and \
`<Struct as Default>::default()`, derive the `Default`"
if removed_all_fields {
let msg = "to avoid divergence in behavior between `Struct { .. }` and \
`<Struct as Default>::default()`, derive the `Default`";
if let Some(hir::Node::Item(impl_)) = tcx.hir().get_if_local(impl_def_id) {
diag.multipart_suggestion_verbose(
msg,
vec![
(tcx.def_span(type_def_id).shrink_to_lo(), "#[derive(Default)] ".to_string()),
(impl_.span, String::new()),
],
Applicability::MachineApplicable,
);
} else {
diag.help(msg);
}
} else {
"use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them \
diverging over time"
});
let msg = "use the default values in the `impl` with `Struct { mandatory_field, .. }` to \
avoid them diverging over time";
diag.help(msg);
}
}

View file

@ -221,6 +221,11 @@ pub enum Suggestion {
/// Remove `r#` from identifier:
/// `format!("{r#foo}")` -> `format!("{foo}")`
RemoveRawIdent(InnerSpan),
/// Reorder format parameter:
/// `format!("{foo:?#}")` -> `format!("{foo:#?}")`
/// `format!("{foo:?x}")` -> `format!("{foo:x?}")`
/// `format!("{foo:?X}")` -> `format!("{foo:X?}")`
ReorderFormatParameter(InnerSpan, string::String),
}
/// The parser structure for interpreting the input format string. This is
@ -731,6 +736,12 @@ impl<'a> Parser<'a> {
}
} else if self.consume('?') {
spec.ty = "?";
if let Some(&(_, maybe)) = self.cur.peek() {
match maybe {
'#' | 'x' | 'X' => self.suggest_format_parameter(maybe),
_ => (),
}
}
} else {
spec.ty = self.word();
if !spec.ty.is_empty() {
@ -932,6 +943,30 @@ impl<'a> Parser<'a> {
}
}
}
fn suggest_format_parameter(&mut self, c: char) {
let replacement = match c {
'#' => "#?",
'x' => "x?",
'X' => "X?",
_ => return,
};
let Some(pos) = self.consume_pos(c) else {
return;
};
let span = self.span(pos - 1, pos + 1);
let pos = self.to_span_index(pos);
self.errors.insert(0, ParseError {
description: format!("expected `}}`, found `{c}`"),
note: None,
label: "expected `'}'`".into(),
span: pos.to(pos),
secondary_label: None,
suggestion: Suggestion::ReorderFormatParameter(span, format!("{replacement}")),
})
}
}
/// Finds the indices of all characters that have been processed and differ between the actual

View file

@ -445,9 +445,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
/// When after several dereferencing, the reference satisfies the trait
/// bound. This function provides dereference suggestion for this
/// specific situation.
/// Provide a suggestion to dereference arguments to functions and binary operators, if that
/// would satisfy trait bounds.
pub(super) fn suggest_dereferences(
&self,
obligation: &PredicateObligation<'tcx>,
@ -461,127 +460,100 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
&& let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)
{
// Suggest dereferencing the argument to a function/method call if possible
// Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)
let mut real_trait_pred = trait_pred;
while let Some((parent_code, parent_trait_pred)) = code.parent() {
code = parent_code;
if let Some(parent_trait_pred) = parent_trait_pred {
real_trait_pred = parent_trait_pred;
}
}
// We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
// `ReBound`, and we don't particularly care about the regions.
let real_ty =
self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());
// We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
// `ReBound`, and we don't particularly care about the regions.
let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());
if !self.can_eq(obligation.param_env, real_ty, arg_ty) {
return false;
}
if self.can_eq(obligation.param_env, real_ty, arg_ty)
&& let ty::Ref(region, base_ty, mutbl) = *real_ty.kind()
// Potentially, we'll want to place our dereferences under a `&`. We don't try this for
// `&mut`, since we can't be sure users will get the side-effects they want from it.
// If this doesn't work, we'll try removing the `&` in `suggest_remove_reference`.
// FIXME(dianne): this misses the case where users need both to deref and remove `&`s.
// This method could be combined with `TypeErrCtxt::suggest_remove_reference` to handle
// that, similar to what `FnCtxt::suggest_deref_or_ref` does.
let (is_under_ref, base_ty, span) = match expr.kind {
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, subexpr)
if let &ty::Ref(region, base_ty, hir::Mutability::Not) = real_ty.kind() =>
{
let autoderef = (self.autoderef_steps)(base_ty);
if let Some(steps) =
autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
// Re-add the `&`
let ty = Ty::new_ref(self.tcx, region, ty, mutbl);
// Remapping bound vars here
let real_trait_pred_and_ty = real_trait_pred
.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
let obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
real_trait_pred_and_ty,
);
let may_hold = obligations
.iter()
.chain([&obligation])
.all(|obligation| self.predicate_may_hold(obligation))
.then_some(steps);
may_hold
})
{
if steps > 0 {
// Don't care about `&mut` because `DerefMut` is used less
// often and user will not expect that an autoderef happens.
if let hir::Node::Expr(hir::Expr {
kind:
hir::ExprKind::AddrOf(
hir::BorrowKind::Ref,
hir::Mutability::Not,
expr,
),
..
}) = self.tcx.hir_node(*arg_hir_id)
{
let derefs = "*".repeat(steps);
err.span_suggestion_verbose(
expr.span.shrink_to_lo(),
"consider dereferencing here",
derefs,
Applicability::MachineApplicable,
);
return true;
}
}
} else if real_trait_pred != trait_pred {
// This branch addresses #87437.
let span = obligation.cause.span;
// Remapping bound vars here
let real_trait_pred_and_base_ty = real_trait_pred
.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty));
let obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
real_trait_pred_and_base_ty,
);
let sized_obligation = Obligation::new(
self.tcx,
obligation.cause.clone(),
obligation.param_env,
ty::TraitRef::new(
self.tcx,
self.tcx.require_lang_item(
hir::LangItem::Sized,
Some(obligation.cause.span),
),
[base_ty],
),
);
if self.predicate_may_hold(&obligation)
&& self.predicate_must_hold_modulo_regions(&sized_obligation)
// Do not suggest * if it is already a reference,
// will suggest removing the borrow instead in that case.
&& !matches!(expr.kind, hir::ExprKind::AddrOf(..))
{
let call_node = self.tcx.hir_node(*call_hir_id);
let msg = "consider dereferencing here";
let is_receiver = matches!(
call_node,
Node::Expr(hir::Expr {
kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),
..
})
if receiver_expr.hir_id == *arg_hir_id
);
if is_receiver {
err.multipart_suggestion_verbose(
msg,
vec![
(span.shrink_to_lo(), "(*".to_string()),
(span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
)
} else {
err.span_suggestion_verbose(
span.shrink_to_lo(),
msg,
'*',
Applicability::MachineApplicable,
)
};
return true;
}
}
(Some(region), base_ty, subexpr.span)
}
// Don't suggest `*&mut`, etc.
hir::ExprKind::AddrOf(..) => return false,
_ => (None, real_ty, obligation.cause.span),
};
let autoderef = (self.autoderef_steps)(base_ty);
let mut is_boxed = base_ty.is_box();
if let Some(steps) = autoderef.into_iter().position(|(mut ty, obligations)| {
// Ensure one of the following for dereferencing to be valid: we're passing by
// reference, `ty` is `Copy`, or we're moving out of a (potentially nested) `Box`.
let can_deref = is_under_ref.is_some()
|| self.type_is_copy_modulo_regions(obligation.param_env, ty)
|| ty.is_numeric() // for inference vars (presumably but not provably `Copy`)
|| is_boxed && self.type_is_sized_modulo_regions(obligation.param_env, ty);
is_boxed &= ty.is_box();
// Re-add the `&` if necessary
if let Some(region) = is_under_ref {
ty = Ty::new_ref(self.tcx, region, ty, hir::Mutability::Not);
}
// Remapping bound vars here
let real_trait_pred_and_ty =
real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
let obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
real_trait_pred_and_ty,
);
can_deref
&& obligations
.iter()
.chain([&obligation])
.all(|obligation| self.predicate_may_hold(obligation))
}) && steps > 0
{
let derefs = "*".repeat(steps);
let msg = "consider dereferencing here";
let call_node = self.tcx.hir_node(*call_hir_id);
let is_receiver = matches!(
call_node,
Node::Expr(hir::Expr {
kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),
..
})
if receiver_expr.hir_id == *arg_hir_id
);
if is_receiver {
err.multipart_suggestion_verbose(
msg,
vec![
(span.shrink_to_lo(), format!("({derefs}")),
(span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
)
} else {
err.span_suggestion_verbose(
span.shrink_to_lo(),
msg,
derefs,
Applicability::MachineApplicable,
)
};
return true;
}
} else if let (
ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. },

View file

@ -288,8 +288,6 @@ cfg_if::cfg_if! {
}
}
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#[allow(static_mut_refs)]
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
use core::intrinsics::atomic_store_seqcst;

View file

@ -401,12 +401,13 @@ impl Step for RustAnalyzer {
macro_rules! tool_check_step {
(
$name:ident,
$display_name:literal,
$path:literal,
$($alias:literal, )*
$source_type:path
$(, $default:literal )?
$name:ident {
// The part of this path after the final '/' is also used as a display name.
path: $path:literal
$(, alt_path: $alt_path:literal )*
$(, default: $default:literal )?
$( , )?
}
) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
@ -416,11 +417,11 @@ macro_rules! tool_check_step {
impl Step for $name {
type Output = ();
const ONLY_HOSTS: bool = true;
/// don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?;
/// Most of the tool-checks using this macro are run by default.
const DEFAULT: bool = true $( && $default )?;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&[ $path, $($alias),* ])
run.paths(&[ $path, $( $alt_path ),* ])
}
fn make_run(run: RunConfig<'_>) {
@ -428,82 +429,71 @@ macro_rules! tool_check_step {
}
fn run(self, builder: &Builder<'_>) {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;
builder.ensure(Rustc::new(target, builder));
let mut cargo = prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
target,
builder.kind,
$path,
$source_type,
&[],
);
// For ./x.py clippy, don't run with --all-targets because
// linting tests and benchmarks can produce very noisy results
if builder.kind != Kind::Clippy {
cargo.arg("--all-targets");
}
let _guard = builder.msg_check(&format!("{} artifacts", $display_name), target);
run_cargo(
builder,
cargo,
builder.config.free_args.clone(),
&stamp(builder, compiler, target),
vec![],
true,
false,
);
/// Cargo's output path in a given stage, compiled by a particular
/// compiler for the specified target.
fn stamp(
builder: &Builder<'_>,
compiler: Compiler,
target: TargetSelection,
) -> PathBuf {
builder
.cargo_out(compiler, Mode::ToolRustc, target)
.join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
}
let Self { target } = self;
run_tool_check_step(builder, target, stringify!($name), $path);
}
}
};
}
}
tool_check_step!(Rustdoc, "rustdoc", "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
/// Used by the implementation of `Step::run` in `tool_check_step!`.
fn run_tool_check_step(
builder: &Builder<'_>,
target: TargetSelection,
step_type_name: &str,
path: &str,
) {
let display_name = path.rsplit('/').next().unwrap();
let compiler = builder.compiler(builder.top_stage, builder.config.build);
builder.ensure(Rustc::new(target, builder));
let mut cargo = prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
target,
builder.kind,
path,
// Currently, all of the tools that use this macro/function are in-tree.
// If support for out-of-tree tools is re-added in the future, those
// steps should probably be marked non-default so that the default
// checks aren't affected by toolstate being broken.
SourceType::InTree,
&[],
);
// For ./x.py clippy, don't run with --all-targets because
// linting tests and benchmarks can produce very noisy results
if builder.kind != Kind::Clippy {
cargo.arg("--all-targets");
}
let stamp = builder
.cargo_out(compiler, Mode::ToolRustc, target)
.join(format!(".{}-check.stamp", step_type_name.to_lowercase()));
let _guard = builder.msg_check(format!("{display_name} artifacts"), target);
run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
}
tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc" });
// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
// of a submodule. Since the SourceType only drives the deny-warnings
// behavior, treat it as in-tree so that any new warnings in clippy will be
// rejected.
tool_check_step!(Clippy, "clippy", "src/tools/clippy", SourceType::InTree);
tool_check_step!(Miri, "miri", "src/tools/miri", SourceType::InTree);
tool_check_step!(CargoMiri, "cargo-miri", "src/tools/miri/cargo-miri", SourceType::InTree);
tool_check_step!(Rls, "rls", "src/tools/rls", SourceType::InTree);
tool_check_step!(Rustfmt, "rustfmt", "src/tools/rustfmt", SourceType::InTree);
tool_check_step!(
MiroptTestTools,
"miropt-test-tools",
"src/tools/miropt-test-tools",
SourceType::InTree
);
tool_check_step!(
TestFloatParse,
"test-float-parse",
"src/etc/test-float-parse",
SourceType::InTree
);
tool_check_step!(Clippy { path: "src/tools/clippy" });
tool_check_step!(Miri { path: "src/tools/miri" });
tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri" });
tool_check_step!(Rls { path: "src/tools/rls" });
tool_check_step!(Rustfmt { path: "src/tools/rustfmt" });
tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" });
tool_check_step!(TestFloatParse { path: "src/etc/test-float-parse" });
tool_check_step!(Bootstrap, "bootstrap", "src/bootstrap", SourceType::InTree, false);
tool_check_step!(Bootstrap { path: "src/bootstrap", default: false });
// Compiletest is implicitly "checked" when it gets built in order to run tests,
// so this is mainly for people working on compiletest to run locally.
tool_check_step!(Compiletest, "compiletest", "src/tools/compiletest", SourceType::InTree, false);
tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false });
/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.

View file

@ -5,7 +5,10 @@ LL | map["peter"].clear();
| ^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>`
= help: to modify a `HashMap<&str, String>`, use `.get_mut()`, `.insert()` or the entry API
help: to modify a `HashMap<&str, String>` use `.get_mut()`
|
LL | if let Some(val) = map.get_mut("peter") { val.clear(); };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~ +++
error[E0594]: cannot assign to data in an index of `HashMap<&str, String>`
--> $DIR/index-mut-help.rs:11:5
@ -14,12 +17,12 @@ LL | map["peter"] = "0".to_string();
| ^^^^^^^^^^^^ cannot assign
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>`
help: to modify a `HashMap<&str, String>`, use `.get_mut()`, `.insert()` or the entry API
help: use `.insert()` to insert a value into a `HashMap<&str, String>`, `.get_mut()` to modify it, or the entry API for more flexibility
|
LL | map.insert("peter", "0".to_string());
| ~~~~~~~~ ~ +
LL | map.get_mut("peter").map(|val| { *val = "0".to_string(); });
| ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ ++++
LL | if let Some(val) = map.get_mut("peter") { *val = "0".to_string(); };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++
LL | let val = map.entry("peter").or_insert("0".to_string());
| +++++++++ ~~~~~~~ ~~~~~~~~~~~~ +

View file

@ -5,12 +5,12 @@ LL | map[&0] = 1;
| ^^^^^^^^^^^ cannot assign
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `BTreeMap<u32, u32>`
help: to modify a `BTreeMap<u32, u32>`, use `.get_mut()`, `.insert()` or the entry API
help: use `.insert()` to insert a value into a `BTreeMap<u32, u32>`, `.get_mut()` to modify it, or the entry API for more flexibility
|
LL | map.insert(&0, 1);
| ~~~~~~~~ ~ +
LL | map.get_mut(&0).map(|val| { *val = 1; });
| ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ ++++
LL | if let Some(val) = map.get_mut(&0) { *val = 1; };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++
LL | let val = map.entry(&0).or_insert(1);
| +++++++++ ~~~~~~~ ~~~~~~~~~~~~ +

View file

@ -5,12 +5,12 @@ LL | map[&0] = 1;
| ^^^^^^^^^^^ cannot assign
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `BTreeMap<u32, u32>`
help: to modify a `BTreeMap<u32, u32>`, use `.get_mut()`, `.insert()` or the entry API
help: use `.insert()` to insert a value into a `BTreeMap<u32, u32>`, `.get_mut()` to modify it, or the entry API for more flexibility
|
LL | map.insert(&0, 1);
| ~~~~~~~~ ~ +
LL | map.get_mut(&0).map(|val| { *val = 1; });
| ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ ++++
LL | if let Some(val) = map.get_mut(&0) { *val = 1; };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++
LL | let val = map.entry(&0).or_insert(1);
| +++++++++ ~~~~~~~ ~~~~~~~~~~~~ +

View file

@ -0,0 +1,25 @@
//! Regression test for https://github.com/rust-lang/rust/issues/129966
//!
//! Ensure we provide suggestion for wrongly ordered format parameters.
//@ run-rustfix
#![allow(dead_code)]
#[derive(Debug)]
struct Foo(u8, u8);
fn main() {
let f = Foo(1, 2);
println!("{f:#?}");
//~^ ERROR invalid format string: expected `}`, found `#`
//~| HELP did you mean `#?`?
println!("{f:x?}");
//~^ ERROR invalid format string: expected `}`, found `x`
//~| HELP did you mean `x?`?
println!("{f:X?}");
//~^ ERROR invalid format string: expected `}`, found `X`
//~| HELP did you mean `X?`?
}

View file

@ -0,0 +1,25 @@
//! Regression test for https://github.com/rust-lang/rust/issues/129966
//!
//! Ensure we provide suggestion for wrongly ordered format parameters.
//@ run-rustfix
#![allow(dead_code)]
#[derive(Debug)]
struct Foo(u8, u8);
fn main() {
let f = Foo(1, 2);
println!("{f:?#}");
//~^ ERROR invalid format string: expected `}`, found `#`
//~| HELP did you mean `#?`?
println!("{f:?x}");
//~^ ERROR invalid format string: expected `}`, found `x`
//~| HELP did you mean `x?`?
println!("{f:?X}");
//~^ ERROR invalid format string: expected `}`, found `X`
//~| HELP did you mean `X?`?
}

View file

@ -0,0 +1,35 @@
error: invalid format string: expected `}`, found `#`
--> $DIR/suggest-wrongly-order-format-parameter.rs:14:19
|
LL | println!("{f:?#}");
| ^ expected `'}'` in format string
|
help: did you mean `#?`?
|
LL | println!("{f:#?}");
| ~~
error: invalid format string: expected `}`, found `x`
--> $DIR/suggest-wrongly-order-format-parameter.rs:18:19
|
LL | println!("{f:?x}");
| ^ expected `'}'` in format string
|
help: did you mean `x?`?
|
LL | println!("{f:x?}");
| ~~
error: invalid format string: expected `}`, found `X`
--> $DIR/suggest-wrongly-order-format-parameter.rs:22:19
|
LL | println!("{f:?X}");
| ^ expected `'}'` in format string
|
help: did you mean `X?`?
|
LL | println!("{f:X?}");
| ~~
error: aborting due to 3 previous errors

View file

@ -5,12 +5,12 @@ LL | map[&0] = 1;
| ^^^^^^^^^^^ cannot assign
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<u32, u32>`
help: to modify a `HashMap<u32, u32>`, use `.get_mut()`, `.insert()` or the entry API
help: use `.insert()` to insert a value into a `HashMap<u32, u32>`, `.get_mut()` to modify it, or the entry API for more flexibility
|
LL | map.insert(&0, 1);
| ~~~~~~~~ ~ +
LL | map.get_mut(&0).map(|val| { *val = 1; });
| ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ ++++
LL | if let Some(val) = map.get_mut(&0) { *val = 1; };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++
LL | let val = map.entry(&0).or_insert(1);
| +++++++++ ~~~~~~~ ~~~~~~~~~~~~ +

View file

@ -5,7 +5,10 @@ LL | things[src.as_str()].sort();
| ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<String, Vec<String>>`
= help: to modify a `HashMap<String, Vec<String>>`, use `.get_mut()`, `.insert()` or the entry API
help: to modify a `HashMap<String, Vec<String>>` use `.get_mut()`
|
LL | if let Some(val) = things.get_mut(src.as_str()) { val.sort(); };
| ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~ +++
error: aborting due to 1 previous error

View file

@ -12,6 +12,10 @@ note: required by a bound in `bar`
|
LL | fn bar<T: Send>(_: T) {}
| ^^^^ required by this bound in `bar`
help: consider dereferencing here
|
LL | bar(*x);
| +
error: aborting due to 1 previous error

View file

@ -31,7 +31,10 @@ LL | | y: 0,
LL | | }
| |_^
|
= help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
|
LL ~ #[derive(Default)] struct B {
|
error: `Default` impl doesn't use the declared default field values
--> $DIR/manual-default-impl-could-be-derived.rs:43:1

View file

@ -86,6 +86,10 @@ note: required by a bound in `f_send`
|
LL | fn f_send<T: Send>(t: T) {}
| ^^^^ required by this bound in `f_send`
help: consider dereferencing here
|
LL | f_send(*rc);
| +
error: aborting due to 5 previous errors

View file

@ -0,0 +1,37 @@
//@ run-rustfix
//! diagnostic test for #90997.
//! test that E0277 suggests dereferences to satisfy bounds when the referent is `Copy` or boxed.
use std::ops::Deref;
trait Test {
fn test(self);
}
fn consume_test(x: impl Test) { x.test() }
impl Test for u32 {
fn test(self) {}
}
struct MyRef(u32);
impl Deref for MyRef {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct NonCopy;
impl Test for NonCopy {
fn test(self) {}
}
fn main() {
let my_ref = MyRef(0);
consume_test(*my_ref);
//~^ ERROR the trait bound `MyRef: Test` is not satisfied
//~| SUGGESTION *
let nested_box = Box::new(Box::new(Box::new(NonCopy)));
consume_test(***nested_box);
//~^ ERROR the trait bound `Box<Box<Box<NonCopy>>>: Test` is not satisfied
//~| SUGGESTION ***
}

View file

@ -0,0 +1,37 @@
//@ run-rustfix
//! diagnostic test for #90997.
//! test that E0277 suggests dereferences to satisfy bounds when the referent is `Copy` or boxed.
use std::ops::Deref;
trait Test {
fn test(self);
}
fn consume_test(x: impl Test) { x.test() }
impl Test for u32 {
fn test(self) {}
}
struct MyRef(u32);
impl Deref for MyRef {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct NonCopy;
impl Test for NonCopy {
fn test(self) {}
}
fn main() {
let my_ref = MyRef(0);
consume_test(my_ref);
//~^ ERROR the trait bound `MyRef: Test` is not satisfied
//~| SUGGESTION *
let nested_box = Box::new(Box::new(Box::new(NonCopy)));
consume_test(nested_box);
//~^ ERROR the trait bound `Box<Box<Box<NonCopy>>>: Test` is not satisfied
//~| SUGGESTION ***
}

View file

@ -0,0 +1,39 @@
error[E0277]: the trait bound `MyRef: Test` is not satisfied
--> $DIR/deref-argument.rs:29:18
|
LL | consume_test(my_ref);
| ------------ ^^^^^^ the trait `Test` is not implemented for `MyRef`
| |
| required by a bound introduced by this call
|
note: required by a bound in `consume_test`
--> $DIR/deref-argument.rs:9:25
|
LL | fn consume_test(x: impl Test) { x.test() }
| ^^^^ required by this bound in `consume_test`
help: consider dereferencing here
|
LL | consume_test(*my_ref);
| +
error[E0277]: the trait bound `Box<Box<Box<NonCopy>>>: Test` is not satisfied
--> $DIR/deref-argument.rs:34:18
|
LL | consume_test(nested_box);
| ------------ ^^^^^^^^^^ the trait `Test` is not implemented for `Box<Box<Box<NonCopy>>>`
| |
| required by a bound introduced by this call
|
note: required by a bound in `consume_test`
--> $DIR/deref-argument.rs:9:25
|
LL | fn consume_test(x: impl Test) { x.test() }
| ^^^^ required by this bound in `consume_test`
help: consider dereferencing here
|
LL | consume_test(***nested_box);
| +++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.