Skip to content

Implement field shorthands in struct literal expressions. #11994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}

1 change: 1 addition & 0 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
@@ -817,6 +817,7 @@ pub struct Field {
pub name: Spanned<Name>,
pub expr: P<Expr>,
pub span: Span,
pub is_shorthand: bool,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
6 changes: 4 additions & 2 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
@@ -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()
},
1 change: 1 addition & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
@@ -900,6 +900,7 @@ pub struct Field {
pub ident: SpannedIdent,
pub expr: P<Expr>,
pub span: Span,
pub is_shorthand: bool,
}

pub type SpannedIdent = Spanned<Ident>;
2 changes: 1 addition & 1 deletion src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
@@ -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::Expr>) -> 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<ast::Field>) -> P<ast::Expr> {
self.expr(span, ast::ExprKind::Struct(path, fields, None))
11 changes: 11 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -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);
9 changes: 5 additions & 4 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
@@ -823,11 +823,12 @@ pub fn noop_fold_struct_field<T: Folder>(f: StructField, fld: &mut T) -> StructF
}
}

pub fn noop_fold_field<T: Folder>(Field {ident, expr, span}: Field, folder: &mut T) -> Field {
pub fn noop_fold_field<T: Folder>(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,
}
}

29 changes: 21 additions & 8 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
@@ -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,
})
}

6 changes: 4 additions & 2 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
@@ -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()
},
24 changes: 24 additions & 0 deletions src/test/compile-fail/feature-gate-field-init-shorthand.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
};
}
24 changes: 24 additions & 0 deletions src/test/compile-fail/struct-fields-shorthand-unresolved.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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`
};
}
24 changes: 24 additions & 0 deletions src/test/compile-fail/struct-fields-shorthand.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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`
};
}

2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-with-2.rs
Original file line number Diff line number Diff line change
@@ -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`
}
19 changes: 19 additions & 0 deletions src/test/parse-fail/struct-field-numeric-shorthand.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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`
}
37 changes: 37 additions & 0 deletions src/test/run-pass/struct-field-shorthand.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}