Auto merge of #95382 - Dylan-DPC:rollup-bebyfd1, r=Dylan-DPC
Rollup of 5 pull requests Successful merges: - #94939 (diagnostics: suggest missing comma in bad FRU syntax) - #95120 (Implement `apply_switch_int_edge_effects` for backward analyses) - #95364 (Add long error explanation for E0667) - #95366 (Remove test files with duplicated checksums) - #95368 (Fix typo in `String::try_reserve_exact` docs) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
62523045ec
@ -394,6 +394,7 @@ E0663: include_str!("./error_codes/E0663.md"),
|
||||
E0664: include_str!("./error_codes/E0664.md"),
|
||||
E0665: include_str!("./error_codes/E0665.md"),
|
||||
E0666: include_str!("./error_codes/E0666.md"),
|
||||
E0667: include_str!("./error_codes/E0667.md"),
|
||||
E0668: include_str!("./error_codes/E0668.md"),
|
||||
E0669: include_str!("./error_codes/E0669.md"),
|
||||
E0670: include_str!("./error_codes/E0670.md"),
|
||||
@ -633,7 +634,6 @@ E0787: include_str!("./error_codes/E0787.md"),
|
||||
// attribute
|
||||
E0640, // infer outlives requirements
|
||||
// E0645, // trait aliases not finished
|
||||
E0667, // `impl Trait` in projections
|
||||
// E0694, // an unknown tool name found in scoped attributes
|
||||
// E0702, // replaced with a generic attribute input check
|
||||
// E0707, // multiple elided lifetimes used in arguments of `async fn`
|
||||
|
18
compiler/rustc_error_codes/src/error_codes/E0667.md
Normal file
18
compiler/rustc_error_codes/src/error_codes/E0667.md
Normal file
@ -0,0 +1,18 @@
|
||||
`impl Trait` is not allowed in path parameters.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0667
|
||||
fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
|
||||
x.next().unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
You cannot use `impl Trait` in path parameters. If you want something
|
||||
equivalent, you can do this instead:
|
||||
|
||||
```
|
||||
fn some_fn<T: Iterator>(mut x: T) -> T::Item { // ok!
|
||||
x.next().unwrap()
|
||||
}
|
||||
```
|
@ -45,6 +45,7 @@ use std::{iter, mem, option};
|
||||
use self::graph_cyclic_cache::GraphIsCyclicCache;
|
||||
use self::predecessors::{PredecessorCache, Predecessors};
|
||||
pub use self::query::*;
|
||||
use self::switch_sources::{SwitchSourceCache, SwitchSources};
|
||||
|
||||
pub mod coverage;
|
||||
mod generic_graph;
|
||||
@ -58,6 +59,7 @@ mod predecessors;
|
||||
pub mod pretty;
|
||||
mod query;
|
||||
pub mod spanview;
|
||||
mod switch_sources;
|
||||
pub mod tcx;
|
||||
pub mod terminator;
|
||||
pub use terminator::*;
|
||||
@ -296,6 +298,7 @@ pub struct Body<'tcx> {
|
||||
pub is_polymorphic: bool,
|
||||
|
||||
predecessor_cache: PredecessorCache,
|
||||
switch_source_cache: SwitchSourceCache,
|
||||
is_cyclic: GraphIsCyclicCache,
|
||||
|
||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||
@ -344,6 +347,7 @@ impl<'tcx> Body<'tcx> {
|
||||
required_consts: Vec::new(),
|
||||
is_polymorphic: false,
|
||||
predecessor_cache: PredecessorCache::new(),
|
||||
switch_source_cache: SwitchSourceCache::new(),
|
||||
is_cyclic: GraphIsCyclicCache::new(),
|
||||
tainted_by_errors,
|
||||
};
|
||||
@ -372,6 +376,7 @@ impl<'tcx> Body<'tcx> {
|
||||
var_debug_info: Vec::new(),
|
||||
is_polymorphic: false,
|
||||
predecessor_cache: PredecessorCache::new(),
|
||||
switch_source_cache: SwitchSourceCache::new(),
|
||||
is_cyclic: GraphIsCyclicCache::new(),
|
||||
tainted_by_errors: None,
|
||||
};
|
||||
@ -392,6 +397,7 @@ impl<'tcx> Body<'tcx> {
|
||||
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
||||
// invalidate the caches.
|
||||
self.predecessor_cache.invalidate();
|
||||
self.switch_source_cache.invalidate();
|
||||
self.is_cyclic.invalidate();
|
||||
&mut self.basic_blocks
|
||||
}
|
||||
@ -401,6 +407,7 @@ impl<'tcx> Body<'tcx> {
|
||||
&mut self,
|
||||
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
||||
self.predecessor_cache.invalidate();
|
||||
self.switch_source_cache.invalidate();
|
||||
self.is_cyclic.invalidate();
|
||||
(&mut self.basic_blocks, &mut self.local_decls)
|
||||
}
|
||||
@ -414,6 +421,7 @@ impl<'tcx> Body<'tcx> {
|
||||
&mut Vec<VarDebugInfo<'tcx>>,
|
||||
) {
|
||||
self.predecessor_cache.invalidate();
|
||||
self.switch_source_cache.invalidate();
|
||||
self.is_cyclic.invalidate();
|
||||
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
||||
}
|
||||
@ -541,6 +549,11 @@ impl<'tcx> Body<'tcx> {
|
||||
self.predecessor_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn switch_sources(&self) -> &SwitchSources {
|
||||
self.switch_source_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dominators(&self) -> Dominators<BasicBlock> {
|
||||
dominators(self)
|
||||
|
82
compiler/rustc_middle/src/mir/switch_sources.rs
Normal file
82
compiler/rustc_middle/src/mir/switch_sources.rs
Normal file
@ -0,0 +1,82 @@
|
||||
//! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after
|
||||
//! `Predecessors`/`PredecessorCache`.
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::OnceCell;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_serialize as serialize;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind};
|
||||
|
||||
pub type SwitchSources = IndexVec<BasicBlock, IndexVec<BasicBlock, SmallVec<[Option<u128>; 1]>>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct SwitchSourceCache {
|
||||
cache: OnceCell<SwitchSources>,
|
||||
}
|
||||
|
||||
impl SwitchSourceCache {
|
||||
#[inline]
|
||||
pub(super) fn new() -> Self {
|
||||
SwitchSourceCache { cache: OnceCell::new() }
|
||||
}
|
||||
|
||||
/// Invalidates the switch source cache.
|
||||
#[inline]
|
||||
pub(super) fn invalidate(&mut self) {
|
||||
self.cache = OnceCell::new();
|
||||
}
|
||||
|
||||
/// Returns the switch sources for this MIR.
|
||||
#[inline]
|
||||
pub(super) fn compute(
|
||||
&self,
|
||||
basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
|
||||
) -> &SwitchSources {
|
||||
self.cache.get_or_init(|| {
|
||||
let mut switch_sources = IndexVec::from_elem(
|
||||
IndexVec::from_elem(SmallVec::new(), basic_blocks),
|
||||
basic_blocks,
|
||||
);
|
||||
for (bb, data) in basic_blocks.iter_enumerated() {
|
||||
if let Some(Terminator {
|
||||
kind: TerminatorKind::SwitchInt { targets, .. }, ..
|
||||
}) = &data.terminator
|
||||
{
|
||||
for (value, target) in targets.iter() {
|
||||
switch_sources[target][bb].push(Some(value));
|
||||
}
|
||||
switch_sources[targets.otherwise()][bb].push(None);
|
||||
}
|
||||
}
|
||||
|
||||
switch_sources
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: serialize::Encoder> serialize::Encodable<S> for SwitchSourceCache {
|
||||
#[inline]
|
||||
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
s.emit_unit()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: serialize::Decoder> serialize::Decodable<D> for SwitchSourceCache {
|
||||
#[inline]
|
||||
fn decode(_: &mut D) -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX> HashStable<CTX> for SwitchSourceCache {
|
||||
#[inline]
|
||||
fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
TrivialTypeFoldableAndLiftImpls! {
|
||||
SwitchSourceCache,
|
||||
}
|
@ -248,6 +248,7 @@ impl Direction for Backward {
|
||||
);
|
||||
propagate(pred, &tmp);
|
||||
}
|
||||
|
||||
mir::TerminatorKind::InlineAsm {
|
||||
destination: Some(dest), ref operands, ..
|
||||
} if dest == bb => {
|
||||
@ -266,6 +267,23 @@ impl Direction for Backward {
|
||||
propagate(pred, &tmp);
|
||||
}
|
||||
|
||||
mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
|
||||
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
|
||||
pred,
|
||||
exit_state,
|
||||
values: &body.switch_sources()[bb][pred],
|
||||
bb,
|
||||
propagate: &mut propagate,
|
||||
effects_applied: false,
|
||||
};
|
||||
|
||||
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
|
||||
|
||||
if !applier.effects_applied {
|
||||
propagate(pred, exit_state)
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore dead unwinds.
|
||||
mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
|
||||
| mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
|
||||
@ -286,6 +304,37 @@ impl Direction for Backward {
|
||||
}
|
||||
}
|
||||
|
||||
struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> {
|
||||
pred: BasicBlock,
|
||||
exit_state: &'a mut D,
|
||||
values: &'a [Option<u128>],
|
||||
bb: BasicBlock,
|
||||
propagate: &'a mut F,
|
||||
|
||||
effects_applied: bool,
|
||||
}
|
||||
|
||||
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
||||
where
|
||||
D: Clone,
|
||||
F: FnMut(BasicBlock, &D),
|
||||
{
|
||||
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
||||
assert!(!self.effects_applied);
|
||||
|
||||
let targets = self.values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
|
||||
|
||||
let mut tmp = None;
|
||||
for target in targets {
|
||||
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
||||
apply_edge_effect(tmp, target);
|
||||
(self.propagate)(self.pred, tmp);
|
||||
}
|
||||
|
||||
self.effects_applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
|
||||
pub struct Forward;
|
||||
|
||||
@ -528,7 +577,7 @@ impl Direction for Forward {
|
||||
}
|
||||
|
||||
SwitchInt { ref targets, ref discr, switch_ty: _ } => {
|
||||
let mut applier = SwitchIntEdgeEffectApplier {
|
||||
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
||||
exit_state,
|
||||
targets,
|
||||
propagate,
|
||||
@ -537,8 +586,11 @@ impl Direction for Forward {
|
||||
|
||||
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
|
||||
|
||||
let SwitchIntEdgeEffectApplier {
|
||||
exit_state, mut propagate, effects_applied, ..
|
||||
let ForwardSwitchIntEdgeEffectsApplier {
|
||||
exit_state,
|
||||
mut propagate,
|
||||
effects_applied,
|
||||
..
|
||||
} = applier;
|
||||
|
||||
if !effects_applied {
|
||||
@ -551,7 +603,7 @@ impl Direction for Forward {
|
||||
}
|
||||
}
|
||||
|
||||
struct SwitchIntEdgeEffectApplier<'a, D, F> {
|
||||
struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
|
||||
exit_state: &'a mut D,
|
||||
targets: &'a SwitchTargets,
|
||||
propagate: F,
|
||||
@ -559,7 +611,7 @@ struct SwitchIntEdgeEffectApplier<'a, D, F> {
|
||||
effects_applied: bool,
|
||||
}
|
||||
|
||||
impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
|
||||
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
||||
where
|
||||
D: Clone,
|
||||
F: FnMut(BasicBlock, &D),
|
||||
|
@ -234,8 +234,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
||||
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
|
||||
/// engine doesn't need to clone the exit state for a block unless
|
||||
/// `SwitchIntEdgeEffects::apply` is actually called.
|
||||
///
|
||||
/// FIXME: This class of effects is not supported for backward dataflow analyses.
|
||||
fn apply_switch_int_edge_effects(
|
||||
&self,
|
||||
_block: BasicBlock,
|
||||
|
@ -28,7 +28,7 @@ pub use self::drop_flag_effects::{
|
||||
pub use self::framework::{
|
||||
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
|
||||
Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
|
||||
ResultsRefCursor, ResultsVisitable, ResultsVisitor,
|
||||
ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
|
||||
};
|
||||
|
||||
use self::move_paths::MoveData;
|
||||
|
@ -32,6 +32,7 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, HirId, QPath};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -1556,7 +1557,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if inaccessible_remaining_fields {
|
||||
self.report_inaccessible_fields(adt_ty, span);
|
||||
} else {
|
||||
self.report_missing_fields(adt_ty, span, remaining_fields);
|
||||
self.report_missing_fields(
|
||||
adt_ty,
|
||||
span,
|
||||
remaining_fields,
|
||||
variant,
|
||||
ast_fields,
|
||||
substs,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1590,6 +1598,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
adt_ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
|
||||
variant: &'tcx ty::VariantDef,
|
||||
ast_fields: &'tcx [hir::ExprField<'tcx>],
|
||||
substs: SubstsRef<'tcx>,
|
||||
) {
|
||||
let len = remaining_fields.len();
|
||||
|
||||
@ -1615,7 +1626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
struct_span_err!(
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0063,
|
||||
@ -1624,9 +1635,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
remaining_fields_names,
|
||||
truncated_fields_error,
|
||||
adt_ty
|
||||
)
|
||||
.span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error))
|
||||
.emit();
|
||||
);
|
||||
err.span_label(
|
||||
span,
|
||||
format!("missing {}{}", remaining_fields_names, truncated_fields_error),
|
||||
);
|
||||
|
||||
// If the last field is a range literal, but it isn't supposed to be, then they probably
|
||||
// meant to use functional update syntax.
|
||||
//
|
||||
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
|
||||
if let Some((
|
||||
last,
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::Range, ..),
|
||||
&[ref range_start, ref range_end],
|
||||
_,
|
||||
),
|
||||
)) = ast_fields.last().map(|last| (last, &last.expr.kind)) &&
|
||||
let variant_field =
|
||||
variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) &&
|
||||
let range_def_id = self.tcx.lang_items().range_struct() &&
|
||||
variant_field
|
||||
.and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
|
||||
.map(|adt| adt.did())
|
||||
!= range_def_id
|
||||
{
|
||||
let instead = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_snippet(range_end.expr.span)
|
||||
.map(|s| format!(" from `{s}`"))
|
||||
.unwrap_or(String::new());
|
||||
err.span_suggestion(
|
||||
range_start.span.shrink_to_hi(),
|
||||
&format!("to set the remaining fields{instead}, separate the last named field with a comma"),
|
||||
",".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
||||
/// Report an error for a struct field expression when there are invisible fields.
|
||||
|
@ -1038,7 +1038,7 @@ impl String {
|
||||
}
|
||||
|
||||
/// Tries to reserve the minimum capacity for exactly `additional` more elements to
|
||||
/// be inserted in the given `String`. After calling `reserve_exact`,
|
||||
/// be inserted in the given `String`. After calling `try_reserve_exact`,
|
||||
/// capacity will be greater than or equal to `self.len() + additional`.
|
||||
/// Does nothing if the capacity is already sufficient.
|
||||
///
|
||||
|
@ -1,9 +0,0 @@
|
||||
#![crate_name = "anonexternmod"]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
#[link(name = "rust_test_helpers", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn rust_get_test_int() -> libc::intptr_t;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#![crate_name = "foreign_lib"]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
pub mod rustrt {
|
||||
extern crate libc;
|
||||
|
||||
#[link(name = "rust_test_helpers", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn rust_get_test_int() -> libc::intptr_t;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod rustrt2 {
|
||||
extern crate libc;
|
||||
|
||||
extern "C" {
|
||||
pub fn rust_get_test_int() -> libc::intptr_t;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod rustrt3 {
|
||||
// Different type, but same ABI (on all supported platforms).
|
||||
// Ensures that we don't ICE or trigger LLVM asserts when
|
||||
// importing the same symbol under different types.
|
||||
// See https://github.com/rust-lang/rust/issues/32740.
|
||||
extern "C" {
|
||||
pub fn rust_get_test_int() -> *const u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_uses() {
|
||||
unsafe {
|
||||
let x = rustrt::rust_get_test_int();
|
||||
assert_eq!(x, rustrt2::rust_get_test_int());
|
||||
assert_eq!(x as *const _, rustrt3::rust_get_test_int());
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#![crate_name = "anonexternmod"]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
#[link(name = "rust_test_helpers", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn rust_get_test_int() -> libc::intptr_t;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
pub fn foo(x: &usize) -> usize {
|
||||
*x
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
pub extern "C" fn bar() {
|
||||
}
|
||||
|
||||
pub const foopy: &'static str = "hi there";
|
||||
pub const uint_val: usize = 12;
|
||||
pub const uint_expr: usize = (1 << uint_val) - 1;
|
@ -30,4 +30,5 @@ LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0223`.
|
||||
Some errors have detailed explanations: E0223, E0667.
|
||||
For more information about an error, try `rustc --explain E0223`.
|
||||
|
@ -6,3 +6,4 @@ LL | pub fn demo(_: impl Quux<(), Assoc=<() as Quux<impl Bar>>::Assoc>) { }
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0667`.
|
||||
|
16
src/test/ui/structs/struct-record-suggestion.fixed
Normal file
16
src/test/ui/structs/struct-record-suggestion.fixed
Normal file
@ -0,0 +1,16 @@
|
||||
// run-rustfix
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
struct A {
|
||||
b: u32,
|
||||
c: u64,
|
||||
d: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let q = A { c: 5, .. Default::default() };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR missing fields
|
||||
//~| HELP separate the last named field with a comma
|
||||
let r = A { c: 5, .. Default::default() };
|
||||
assert_eq!(q, r);
|
||||
}
|
16
src/test/ui/structs/struct-record-suggestion.rs
Normal file
16
src/test/ui/structs/struct-record-suggestion.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// run-rustfix
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
struct A {
|
||||
b: u32,
|
||||
c: u64,
|
||||
d: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let q = A { c: 5 .. Default::default() };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR missing fields
|
||||
//~| HELP separate the last named field with a comma
|
||||
let r = A { c: 5, .. Default::default() };
|
||||
assert_eq!(q, r);
|
||||
}
|
24
src/test/ui/structs/struct-record-suggestion.stderr
Normal file
24
src/test/ui/structs/struct-record-suggestion.stderr
Normal file
@ -0,0 +1,24 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/struct-record-suggestion.rs:10:20
|
||||
|
|
||||
LL | let q = A { c: 5 .. Default::default() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range`
|
||||
|
|
||||
= note: expected type `u64`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0063]: missing fields `b` and `d` in initializer of `A`
|
||||
--> $DIR/struct-record-suggestion.rs:10:13
|
||||
|
|
||||
LL | let q = A { c: 5 .. Default::default() };
|
||||
| ^ missing `b` and `d`
|
||||
|
|
||||
help: to set the remaining fields from `Default::default()`, separate the last named field with a comma
|
||||
|
|
||||
LL | let q = A { c: 5, .. Default::default() };
|
||||
| +
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0063, E0308.
|
||||
For more information about an error, try `rustc --explain E0063`.
|
Loading…
x
Reference in New Issue
Block a user