diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 562156e70bd99..11f635847a053 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -543,6 +543,7 @@ impl<'a> LoweringContext<'a> { name: respan(f.ident.span, f.ident.node.name), expr: self.lower_expr(&f.expr), span: f.span, + is_shorthand: f.is_shorthand, } } @@ -1682,6 +1683,7 @@ impl<'a> LoweringContext<'a> { }, span: span, expr: expr, + is_shorthand: false, } } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 1de4355ccdfd4..1ac0a48713a29 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -817,6 +817,7 @@ pub struct Field { pub name: Spanned, pub expr: P, pub span: Span, + pub is_shorthand: bool, } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 90b92beb7a7fb..657c10bab12eb 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1229,8 +1229,10 @@ impl<'a> State<'a> { &fields[..], |s, field| { s.ibox(indent_unit)?; - s.print_name(field.name.node)?; - s.word_space(":")?; + if !field.is_shorthand { + s.print_name(field.name.node)?; + s.word_space(":")?; + } s.print_expr(&field.expr)?; s.end() }, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ae036e66c6903..37e306de325bf 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -900,6 +900,7 @@ pub struct Field { pub ident: SpannedIdent, pub expr: P, pub span: Span, + pub is_shorthand: bool, } pub type SpannedIdent = Spanned; diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index bdbc45471bba2..0ef47bd6daaa8 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -713,7 +713,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr(b.span, ast::ExprKind::Block(b)) } fn field_imm(&self, span: Span, name: Ident, e: P) -> ast::Field { - ast::Field { ident: respan(span, name), expr: e, span: span } + ast::Field { ident: respan(span, name), expr: e, span: span, is_shorthand: false } } fn expr_struct(&self, span: Span, path: ast::Path, fields: Vec) -> P { self.expr(span, ast::ExprKind::Struct(path, fields, None)) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 954fe330b54ca..02580331a99f9 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -306,6 +306,9 @@ declare_features! ( // Allows attributes on lifetime/type formal parameters in generics (RFC 1327) (active, generic_param_attrs, "1.11.0", Some(34761)), + + // Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. + (active, field_init_shorthand, "1.14.0", Some(37340)), ); declare_features! ( @@ -1087,6 +1090,14 @@ impl<'a> Visitor for PostExpansionVisitor<'a> { ast::ExprKind::InPlace(..) => { gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN); } + ast::ExprKind::Struct(_, ref fields, _) => { + for field in fields { + if field.is_shorthand { + gate_feature_post!(&self, field_init_shorthand, field.span, + "struct field shorthands are unstable"); + } + } + } _ => {} } visit::walk_expr(self, e); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 08c0637b2d902..16916ddd5d6a0 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -823,11 +823,12 @@ pub fn noop_fold_struct_field(f: StructField, fld: &mut T) -> StructF } } -pub fn noop_fold_field(Field {ident, expr, span}: Field, folder: &mut T) -> Field { +pub fn noop_fold_field(f: Field, folder: &mut T) -> Field { Field { - ident: respan(ident.span, folder.fold_ident(ident.node)), - expr: folder.fold_expr(expr), - span: folder.new_span(span) + ident: respan(f.ident.span, folder.fold_ident(f.ident.node)), + expr: folder.fold_expr(f.expr), + span: folder.new_span(f.span), + is_shorthand: f.is_shorthand, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 463ec334cc567..a5a081e08bee3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2007,17 +2007,30 @@ impl<'a> Parser<'a> { } } - /// Parse ident COLON expr + /// Parse ident (COLON expr)? pub fn parse_field(&mut self) -> PResult<'a, Field> { let lo = self.span.lo; - let i = self.parse_field_name()?; - let hi = self.prev_span.hi; - self.expect(&token::Colon)?; - let e = self.parse_expr()?; + let hi; + + // Check if a colon exists one ahead. This means we're parsing a fieldname. + let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { + let fieldname = self.parse_field_name()?; + self.bump(); + hi = self.prev_span.hi; + (fieldname, self.parse_expr()?, false) + } else { + let fieldname = self.parse_ident()?; + hi = self.prev_span.hi; + + // Mimic `x: x` for the `x` field shorthand. + let path = ast::Path::from_ident(mk_sp(lo, hi), fieldname); + (fieldname, self.mk_expr(lo, hi, ExprKind::Path(None, path), ThinVec::new()), true) + }; Ok(ast::Field { - ident: spanned(lo, hi, i), - span: mk_sp(lo, e.span.hi), - expr: e, + ident: spanned(lo, hi, fieldname), + span: mk_sp(lo, expr.span.hi), + expr: expr, + is_shorthand: is_shorthand, }) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index c7248fe68fafd..149112133b234 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1893,8 +1893,10 @@ impl<'a> State<'a> { &fields[..], |s, field| { try!(s.ibox(INDENT_UNIT)); - try!(s.print_ident(field.ident.node)); - try!(s.word_space(":")); + if !field.is_shorthand { + try!(s.print_ident(field.ident.node)); + try!(s.word_space(":")); + } try!(s.print_expr(&field.expr)); s.end() }, diff --git a/src/test/compile-fail/feature-gate-field-init-shorthand.rs b/src/test/compile-fail/feature-gate-field-init-shorthand.rs new file mode 100644 index 0000000000000..cd2dae7f461ab --- /dev/null +++ b/src/test/compile-fail/feature-gate-field-init-shorthand.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + x: i32, + y: bool, + z: i32 +} + +fn main() { + let (x, y, z) = (1, true, 2); + let _ = Foo { + x, //~ ERROR struct field shorthands are unstable + y: y, + z //~ ERROR struct field shorthands are unstable + }; +} diff --git a/src/test/compile-fail/struct-fields-shorthand-unresolved.rs b/src/test/compile-fail/struct-fields-shorthand-unresolved.rs new file mode 100644 index 0000000000000..50a43f4a276da --- /dev/null +++ b/src/test/compile-fail/struct-fields-shorthand-unresolved.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(field_init_shorthand)] + +struct Foo { + x: i32, + y: i32 +} + +fn main() { + let x = 0; + let foo = Foo { + x, + y //~ ERROR unresolved name `y` + }; +} diff --git a/src/test/compile-fail/struct-fields-shorthand.rs b/src/test/compile-fail/struct-fields-shorthand.rs new file mode 100644 index 0000000000000..f764322cadbbb --- /dev/null +++ b/src/test/compile-fail/struct-fields-shorthand.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(field_init_shorthand)] + +struct Foo { + x: i32, + y: i32 +} + +fn main() { + let (x, y, z) = (0, 1, 2); + let foo = Foo { + x, y, z //~ ERROR struct `Foo` has no field named `z` + }; +} + diff --git a/src/test/parse-fail/removed-syntax-with-2.rs b/src/test/parse-fail/removed-syntax-with-2.rs index c58f42abb58f0..c4354c0760442 100644 --- a/src/test/parse-fail/removed-syntax-with-2.rs +++ b/src/test/parse-fail/removed-syntax-with-2.rs @@ -18,5 +18,5 @@ fn removed_with() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; - //~^ ERROR expected `:`, found `a` + //~^ ERROR expected one of `,` or `}`, found `a` } diff --git a/src/test/parse-fail/struct-field-numeric-shorthand.rs b/src/test/parse-fail/struct-field-numeric-shorthand.rs new file mode 100644 index 0000000000000..2a5c25d1868f5 --- /dev/null +++ b/src/test/parse-fail/struct-field-numeric-shorthand.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +#![feature(field_init_shorthand)] + +struct Rgb(u8, u8, u8); + +fn main() { + let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` +} diff --git a/src/test/run-pass/struct-field-shorthand.rs b/src/test/run-pass/struct-field-shorthand.rs new file mode 100644 index 0000000000000..fe91db572d20e --- /dev/null +++ b/src/test/run-pass/struct-field-shorthand.rs @@ -0,0 +1,37 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(field_init_shorthand)] + +struct Foo { + x: i32, + y: bool, + z: i32 +} + +struct Bar { + x: i32 +} + +pub fn main() { + let (x, y, z) = (1, true, 2); + let a = Foo { x, y: y, z }; + assert_eq!(a.x, x); + assert_eq!(a.y, y); + assert_eq!(a.z, z); + + let b = Bar { x, }; + assert_eq!(b.x, x); + + let c = Foo { z, y, x }; + assert_eq!(c.x, x); + assert_eq!(c.y, y); + assert_eq!(c.z, z); +}