From 8f1fff04a72f65bc991b0c76b7b7300384b0d33d Mon Sep 17 00:00:00 2001 From: ouz-a Date: Tue, 31 May 2022 21:34:04 +0300 Subject: [PATCH 1/5] get_vtable returns opt instd of unwrpping --- .../rustc_trait_selection/src/traits/util.rs | 14 +++-- compiler/rustc_ty_utils/src/instance.rs | 14 +++-- src/test/ui/traits/vtable/issue-97381.rs | 30 ++++++++++ src/test/ui/traits/vtable/issue-97381.stderr | 15 +++++ src/test/ui/type/type-unsatisfiable.rs | 59 +++++++++++++++++++ .../ui/type/type-unsatisfiable.usage.stderr | 11 ++++ 6 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/traits/vtable/issue-97381.rs create mode 100644 src/test/ui/traits/vtable/issue-97381.stderr create mode 100644 src/test/ui/type/type-unsatisfiable.rs create mode 100644 src/test/ui/type/type-unsatisfiable.usage.stderr diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index f2e31c068a0..3a00c41d90a 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -304,22 +304,24 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( tcx: TyCtxt<'tcx>, object: &super::ImplSourceObjectData<'tcx, N>, method_def_id: DefId, -) -> usize { +) -> Option { let existential_trait_ref = object .upcast_trait_ref .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); let existential_trait_ref = tcx.erase_regions(existential_trait_ref); + // Count number of methods preceding the one we are selecting and // add them to the total offset. - let index = tcx + if let Some(index) = tcx .own_existential_vtable_entries(existential_trait_ref) .iter() .copied() .position(|def_id| def_id == method_def_id) - .unwrap_or_else(|| { - bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id); - }); - object.vtable_base + index + { + Some(object.vtable_base + index) + } else { + None + } } pub fn closure_trait_ref_and_return_type<'tcx>( diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 17eac2bb2c9..c78a5855edb 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -347,11 +347,15 @@ fn resolve_associated_item<'tcx>( _ => None, }, traits::ImplSource::Object(ref data) => { - let index = traits::get_vtable_index_of_object_method(tcx, data, trait_item_id); - Some(Instance { - def: ty::InstanceDef::Virtual(trait_item_id, index), - substs: rcvr_substs, - }) + if let Some(index) = traits::get_vtable_index_of_object_method(tcx, data, trait_item_id) + { + Some(Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + substs: rcvr_substs, + }) + } else { + None + } } traits::ImplSource::Builtin(..) => { if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { diff --git a/src/test/ui/traits/vtable/issue-97381.rs b/src/test/ui/traits/vtable/issue-97381.rs new file mode 100644 index 00000000000..393cf91efc2 --- /dev/null +++ b/src/test/ui/traits/vtable/issue-97381.rs @@ -0,0 +1,30 @@ +use std::ops::Deref; +trait MyTrait: Deref {} +struct MyStruct(u32); +impl MyTrait for MyStruct {} +impl Deref for MyStruct { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +fn get_concrete_value(i: u32) -> MyStruct { + MyStruct(i) +} +fn get_boxed_value(i: u32) -> Box { + Box::new(get_concrete_value(i)) +} +fn main() { + let v = [1, 2, 3] + .iter() + .map(|i| get_boxed_value(*i)) + .collect::>(); + + let el = &v[0]; + + for _ in v { + //~^ ERROR cannot move out of `v` because it is borrowed + println!("{}", ***el > 0); + } +} diff --git a/src/test/ui/traits/vtable/issue-97381.stderr b/src/test/ui/traits/vtable/issue-97381.stderr new file mode 100644 index 00000000000..f88c8716ff7 --- /dev/null +++ b/src/test/ui/traits/vtable/issue-97381.stderr @@ -0,0 +1,15 @@ +error[E0505]: cannot move out of `v` because it is borrowed + --> $DIR/issue-97381.rs:26:14 + | +LL | let el = &v[0]; + | - borrow of `v` occurs here +LL | +LL | for _ in v { + | ^ move out of `v` occurs here +LL | +LL | println!("{}", ***el > 0); + | ---- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/src/test/ui/type/type-unsatisfiable.rs b/src/test/ui/type/type-unsatisfiable.rs new file mode 100644 index 00000000000..7fbbb50dc11 --- /dev/null +++ b/src/test/ui/type/type-unsatisfiable.rs @@ -0,0 +1,59 @@ +// revisions: lib usage +//[lib] compile-flags: --crate-type=lib +//[lib] build-pass + +use std::ops::Sub; +trait Vector2 { + type ScalarType; + + fn from_values(x: Self::ScalarType, y: Self::ScalarType) -> Self + where + Self: Sized; + + fn x(&self) -> Self::ScalarType; + fn y(&self) -> Self::ScalarType; +} + +impl Sub for dyn Vector2 +where + T: Sub, + (dyn Vector2): Sized, +{ + type Output = dyn Vector2; + + fn sub(self, rhs: Self) -> Self::Output { + Self::from_values(self.x() - rhs.x(), self.y() - rhs.y()) + } +} + +struct Vec2 { + x: i32, + y: i32, +} + +impl Vector2 for Vec2 { + type ScalarType = i32; + + fn from_values(x: Self::ScalarType, y: Self::ScalarType) -> Self + where + Self: Sized, + { + Self { x, y } + } + + fn x(&self) -> Self::ScalarType { + self.x + } + fn y(&self) -> Self::ScalarType { + self.y + } +} + +#[cfg(usage)] +fn main() { + let hey: Box> = Box::new(Vec2 { x: 1, y: 2 }); + let word: Box> = Box::new(Vec2 { x: 1, y: 2 }); + + let bar = *hey - *word; + //[usage]~^ ERROR cannot subtract +} diff --git a/src/test/ui/type/type-unsatisfiable.usage.stderr b/src/test/ui/type/type-unsatisfiable.usage.stderr new file mode 100644 index 00000000000..56e2e30afac --- /dev/null +++ b/src/test/ui/type/type-unsatisfiable.usage.stderr @@ -0,0 +1,11 @@ +error[E0369]: cannot subtract `(dyn Vector2 + 'static)` from `dyn Vector2` + --> $DIR/type-unsatisfiable.rs:57:20 + | +LL | let bar = *hey - *word; + | ---- ^ ----- (dyn Vector2 + 'static) + | | + | dyn Vector2 + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0369`. From b13eb61a2cdfac1e094c2a4d608086f40cea5e01 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 7 Jun 2022 02:19:18 -0700 Subject: [PATCH 2/5] Recover missing comma after match arm --- compiler/rustc_parse/src/parser/expr.rs | 51 ++++++++++++++----- .../ui/parser/match-arm-without-braces.rs | 6 +-- .../ui/parser/match-arm-without-braces.stderr | 10 ++-- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b786c52e688..324e04b1981 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2718,13 +2718,12 @@ impl<'a> Parser<'a> { )); } this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]) - .map_err(|mut err| { - match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) { - (Ok(ref expr_lines), Ok(ref arm_start_lines)) - if arm_start_lines.lines[0].end_col - == expr_lines.lines[0].end_col - && expr_lines.lines.len() == 2 - && this.token == token::FatArrow => + .or_else(|mut err| { + if this.token == token::FatArrow { + if let Ok(expr_lines) = sm.span_to_lines(expr.span) + && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) + && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col + && expr_lines.lines.len() == 2 { // We check whether there's any trailing code in the parse span, // if there isn't, we very likely have the following: @@ -2743,15 +2742,41 @@ impl<'a> Parser<'a> { ",".to_owned(), Applicability::MachineApplicable, ); + return Err(err); } - _ => { - err.span_label( - arrow_span, - "while parsing the `match` arm starting here", - ); + } else { + // FIXME(compiler-errors): We could also recover `; PAT =>` here + + // Try to parse a following `PAT =>`, if successful + // then we should recover. + let mut snapshot = this.create_snapshot_for_diagnostic(); + let pattern_follows = snapshot + .parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + ) + .map_err(|err| err.cancel()) + .is_ok(); + if pattern_follows && snapshot.check(&TokenKind::FatArrow) { + err.cancel(); + this.struct_span_err( + hi.shrink_to_hi(), + "expected `,` following `match` arm", + ) + .span_suggestion( + hi.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + return Ok(true); } } - err + err.span_label(arrow_span, "while parsing the `match` arm starting here"); + Err(err) })?; } else { this.eat(&token::Comma); diff --git a/src/test/ui/parser/match-arm-without-braces.rs b/src/test/ui/parser/match-arm-without-braces.rs index 55a88742769..bba38fd0fa4 100644 --- a/src/test/ui/parser/match-arm-without-braces.rs +++ b/src/test/ui/parser/match-arm-without-braces.rs @@ -45,9 +45,9 @@ fn main() { 15; } match S::get(16) { - Some(Val::Foo) => 17 - _ => 18, //~ ERROR expected one of - } + Some(Val::Foo) => 17 //~ ERROR expected `,` following `match` arm + _ => 18, + }; match S::get(19) { Some(Val::Foo) => 20; //~ ERROR `match` arm body without braces diff --git a/src/test/ui/parser/match-arm-without-braces.stderr b/src/test/ui/parser/match-arm-without-braces.stderr index 4831d79ef2b..37d55aa53f8 100644 --- a/src/test/ui/parser/match-arm-without-braces.stderr +++ b/src/test/ui/parser/match-arm-without-braces.stderr @@ -52,15 +52,11 @@ LL ~ { 14; LL ~ 15; } | -error: expected one of `,`, `.`, `?`, `}`, or an operator, found reserved identifier `_` - --> $DIR/match-arm-without-braces.rs:49:9 +error: expected `,` following `match` arm + --> $DIR/match-arm-without-braces.rs:48:29 | LL | Some(Val::Foo) => 17 - | -- - expected one of `,`, `.`, `?`, `}`, or an operator - | | - | while parsing the `match` arm starting here -LL | _ => 18, - | ^ unexpected token + | ^ help: missing a comma here to end this `match` arm: `,` error: `match` arm body without braces --> $DIR/match-arm-without-braces.rs:53:11 From 6277c3a9441a0f2e0c89d479c1f3306567685817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 1 Jun 2022 00:00:00 +0000 Subject: [PATCH 3/5] Preserve unused pointer to address casts --- compiler/rustc_middle/src/mir/mod.rs | 29 +++++++++++++++++-- .../rustc_mir_dataflow/src/impls/liveness.rs | 9 ++---- .../src/dead_store_elimination.rs | 2 +- compiler/rustc_mir_transform/src/simplify.rs | 8 +++-- src/test/mir-opt/simplify-locals.rs | 7 +++++ ...ify_locals.expose_addr.SimplifyLocals.diff | 21 ++++++++++++++ 6 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 src/test/mir-opt/simplify_locals.expose_addr.SimplifyLocals.diff diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f3db359ec33..f382812f163 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2605,9 +2605,34 @@ pub enum Rvalue<'tcx> { static_assert_size!(Rvalue<'_>, 40); impl<'tcx> Rvalue<'tcx> { + /// Returns true if rvalue can be safely removed when the result is unused. #[inline] - pub fn is_pointer_int_cast(&self) -> bool { - matches!(self, Rvalue::Cast(CastKind::PointerExposeAddress, _, _)) + pub fn is_safe_to_remove(&self) -> bool { + match self { + // Pointer to int casts may be side-effects due to exposing the provenance. + // While the model is undecided, we should be conservative. See + // + Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false, + + Rvalue::Use(_) + | Rvalue::Repeat(_, _) + | Rvalue::Ref(_, _, _) + | Rvalue::ThreadLocalRef(_) + | Rvalue::AddressOf(_, _) + | Rvalue::Len(_) + | Rvalue::Cast( + CastKind::Misc | CastKind::Pointer(_) | CastKind::PointerFromExposedAddress, + _, + _, + ) + | Rvalue::BinaryOp(_, _) + | Rvalue::CheckedBinaryOp(_, _) + | Rvalue::NullaryOp(_, _) + | Rvalue::UnaryOp(_, _) + | Rvalue::Discriminant(_) + | Rvalue::Aggregate(_, _) + | Rvalue::ShallowInitBox(_, _) => true, + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 7076fbe1bdb..9b62ee5473c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -244,13 +244,10 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { // Compute the place that we are storing to, if any let destination = match &statement.kind { StatementKind::Assign(assign) => { - if assign.1.is_pointer_int_cast() { - // Pointer to int casts may be side-effects due to exposing the provenance. - // While the model is undecided, we should be conservative. See - // - None - } else { + if assign.1.is_safe_to_remove() { Some(assign.0) + } else { + None } } StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 8becac34ed7..779f3c77815 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -34,7 +34,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { let loc = Location { block: bb, statement_index }; if let StatementKind::Assign(assign) = &statement.kind { - if assign.1.is_pointer_int_cast() { + if !assign.1.is_safe_to_remove() { continue; } } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 72e08343925..8a78ea5c82b 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -494,8 +494,12 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} StatementKind::Assign(box (ref place, ref rvalue)) => { - self.visit_lhs(place, location); - self.visit_rvalue(rvalue, location); + if rvalue.is_safe_to_remove() { + self.visit_lhs(place, location); + self.visit_rvalue(rvalue, location); + } else { + self.super_statement(statement, location); + } } StatementKind::SetDiscriminant { ref place, variant_index: _ } diff --git a/src/test/mir-opt/simplify-locals.rs b/src/test/mir-opt/simplify-locals.rs index 5862cf2eb29..f6bf396cd05 100644 --- a/src/test/mir-opt/simplify-locals.rs +++ b/src/test/mir-opt/simplify-locals.rs @@ -62,6 +62,12 @@ fn t4() -> u32 { unsafe { X + 1 } } +// EMIT_MIR simplify_locals.expose_addr.SimplifyLocals.diff +fn expose_addr(p: *const usize) { + // Used pointer to address cast. Has a side effect of exposing the provenance. + p as usize; +} + fn main() { c(); d1(); @@ -71,4 +77,5 @@ fn main() { t2(); t3(); t4(); + expose_addr(&0); } diff --git a/src/test/mir-opt/simplify_locals.expose_addr.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals.expose_addr.SimplifyLocals.diff new file mode 100644 index 00000000000..93d77ad40aa --- /dev/null +++ b/src/test/mir-opt/simplify_locals.expose_addr.SimplifyLocals.diff @@ -0,0 +1,21 @@ +- // MIR for `expose_addr` before SimplifyLocals ++ // MIR for `expose_addr` after SimplifyLocals + + fn expose_addr(_1: *const usize) -> () { + debug p => _1; // in scope 0 at $DIR/simplify-locals.rs:66:16: 66:17 + let mut _0: (); // return place in scope 0 at $DIR/simplify-locals.rs:66:33: 66:33 + let _2: usize; // in scope 0 at $DIR/simplify-locals.rs:68:5: 68:15 + let mut _3: *const usize; // in scope 0 at $DIR/simplify-locals.rs:68:5: 68:6 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify-locals.rs:68:5: 68:15 + StorageLive(_3); // scope 0 at $DIR/simplify-locals.rs:68:5: 68:6 + _3 = _1; // scope 0 at $DIR/simplify-locals.rs:68:5: 68:6 + _2 = move _3 as usize (PointerExposeAddress); // scope 0 at $DIR/simplify-locals.rs:68:5: 68:15 + StorageDead(_3); // scope 0 at $DIR/simplify-locals.rs:68:14: 68:15 + StorageDead(_2); // scope 0 at $DIR/simplify-locals.rs:68:15: 68:16 + _0 = const (); // scope 0 at $DIR/simplify-locals.rs:66:33: 69:2 + return; // scope 0 at $DIR/simplify-locals.rs:69:2: 69:2 + } + } + From 5dd5244423d36972ee95a236eea650ba3d74843a Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 7 Jun 2022 19:24:09 -0400 Subject: [PATCH 4/5] Use repr(C) when depending on struct layout in ptr tests --- library/core/tests/ptr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 40b2b49bdbd..3e2956eac87 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -19,6 +19,7 @@ fn test_const_from_raw_parts() { #[test] fn test() { unsafe { + #[repr(C)] struct Pair { fst: isize, snd: isize, From b7ed8601084e58e6919d62c3b32d81c1766ce576 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 7 Jun 2022 00:13:50 -0700 Subject: [PATCH 5/5] recover `import` instead of `use` in item --- compiler/rustc_parse/src/parser/item.rs | 69 +++++++++++++------ .../did_you_mean/use_instead_of_import.fixed | 15 ++++ .../ui/did_you_mean/use_instead_of_import.rs | 15 ++++ .../did_you_mean/use_instead_of_import.stderr | 14 ++++ 4 files changed, 93 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.fixed create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.rs create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6720399aacb..48c3c467bec 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -204,25 +204,7 @@ impl<'a> Parser<'a> { let mut def = || mem::replace(def, Defaultness::Final); let info = if self.eat_keyword(kw::Use) { - // USE ITEM - let tree = self.parse_use_tree()?; - - // If wildcard or glob-like brace syntax doesn't have `;`, - // the user may not know `*` or `{}` should be the last. - if let Err(mut e) = self.expect_semi() { - match tree.kind { - UseTreeKind::Glob => { - e.note("the wildcard token must be last on the path"); - } - UseTreeKind::Nested(..) => { - e.note("glob-like brace syntax must be last on the path"); - } - _ => (), - } - return Err(e); - } - - (Ident::empty(), ItemKind::Use(tree)) + self.parse_use_item()? } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; @@ -288,7 +270,12 @@ impl<'a> Parser<'a> { } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { // MACRO_RULES ITEM self.parse_item_macro_rules(vis, has_bang)? - } else if vis.kind.is_pub() && self.isnt_macro_invocation() { + } else if self.isnt_macro_invocation() + && (self.token.is_ident_named(Symbol::intern("import")) + || self.token.is_ident_named(Symbol::intern("using"))) + { + return self.recover_import_as_use(); + } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); } else if macros_allowed && self.check_path() { @@ -300,6 +287,48 @@ impl<'a> Parser<'a> { Ok(Some(info)) } + fn recover_import_as_use(&mut self) -> PResult<'a, Option<(Ident, ItemKind)>> { + let span = self.token.span; + let token_name = super::token_descr(&self.token); + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_use_item() { + Ok(u) => { + self.struct_span_err(span, format!("expected item, found {token_name}")) + .span_suggestion_short( + span, + "items are imported using the `use` keyword", + "use".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + Ok(Some(u)) + } + Err(e) => { + e.cancel(); + self.restore_snapshot(snapshot); + Ok(None) + } + } + } + + fn parse_use_item(&mut self) -> PResult<'a, (Ident, ItemKind)> { + let tree = self.parse_use_tree()?; + if let Err(mut e) = self.expect_semi() { + match tree.kind { + UseTreeKind::Glob => { + e.note("the wildcard token must be last on the path"); + } + UseTreeKind::Nested(..) => { + e.note("glob-like brace syntax must be last on the path"); + } + _ => (), + } + return Err(e); + } + Ok((Ident::empty(), ItemKind::Use(tree))) + } + /// When parsing a statement, would the start of a path be an item? pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` diff --git a/src/test/ui/did_you_mean/use_instead_of_import.fixed b/src/test/ui/did_you_mean/use_instead_of_import.fixed new file mode 100644 index 00000000000..87d453e1565 --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +use std::{ + //~^ ERROR expected item, found `import` + io::Write, + rc::Rc, +}; + +pub use std::io; +//~^ ERROR expected item, found `using` + +fn main() { + let x = Rc::new(1); + let _ = write!(io::stdout(), "{:?}", x); +} diff --git a/src/test/ui/did_you_mean/use_instead_of_import.rs b/src/test/ui/did_you_mean/use_instead_of_import.rs new file mode 100644 index 00000000000..59e83732328 --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.rs @@ -0,0 +1,15 @@ +// run-rustfix + +import std::{ + //~^ ERROR expected item, found `import` + io::Write, + rc::Rc, +}; + +pub using std::io; +//~^ ERROR expected item, found `using` + +fn main() { + let x = Rc::new(1); + let _ = write!(io::stdout(), "{:?}", x); +} diff --git a/src/test/ui/did_you_mean/use_instead_of_import.stderr b/src/test/ui/did_you_mean/use_instead_of_import.stderr new file mode 100644 index 00000000000..b22954af80f --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.stderr @@ -0,0 +1,14 @@ +error: expected item, found `import` + --> $DIR/use_instead_of_import.rs:3:1 + | +LL | import std::{ + | ^^^^^^ help: items are imported using the `use` keyword + +error: expected item, found `using` + --> $DIR/use_instead_of_import.rs:9:5 + | +LL | pub using std::io; + | ^^^^^ help: items are imported using the `use` keyword + +error: aborting due to 2 previous errors +