Skip to content
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

syntax: enable attributes and cfg on struct fields #38814

Merged
merged 1 commit into from
Jan 12, 2017
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/libsyntax/ast.rs
Original file line number Diff line number Diff line change
@@ -538,6 +538,7 @@ pub struct FieldPat {
/// The pattern the field is destructured to
pub pat: P<Pat>,
pub is_shorthand: bool,
pub attrs: ThinVec<Attribute>,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -815,6 +816,7 @@ pub struct Field {
pub expr: P<Expr>,
pub span: Span,
pub is_shorthand: bool,
pub attrs: ThinVec<Attribute>,
}

pub type SpannedIdent = Spanned<Ident>;
32 changes: 22 additions & 10 deletions src/libsyntax/attr.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ use ast;
use ast::{AttrId, Attribute, Name};
use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
use ast::{Lit, Expr, Item, Local, Stmt, StmtKind};
use codemap::{spanned, dummy_spanned, mk_sp};
use codemap::{Spanned, spanned, dummy_spanned, mk_sp};
use syntax_pos::{Span, BytePos, DUMMY_SP};
use errors::Handler;
use feature_gate::{Features, GatedCfg};
@@ -959,6 +959,13 @@ pub trait HasAttrs: Sized {
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
}

impl<T: HasAttrs> HasAttrs for Spanned<T> {
fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
Spanned { node: self.node.map_attrs(f), span: self.span }
}
}

impl HasAttrs for Vec<Attribute> {
fn attrs(&self) -> &[Attribute] {
&self
@@ -1012,26 +1019,31 @@ impl HasAttrs for StmtKind {
}
}

macro_rules! derive_has_attrs_from_field {
($($ty:path),*) => { derive_has_attrs_from_field!($($ty: .attrs),*); };
($($ty:path : $(.$field:ident)*),*) => { $(
impl HasAttrs for Stmt {
fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
Stmt { id: self.id, node: self.node.map_attrs(f), span: self.span }
}
}

macro_rules! derive_has_attrs {
($($ty:path),*) => { $(
impl HasAttrs for $ty {
fn attrs(&self) -> &[Attribute] {
self $(.$field)* .attrs()
&self.attrs
}

fn map_attrs<F>(mut self, f: F) -> Self
where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
{
self $(.$field)* = self $(.$field)* .map_attrs(f);
self.attrs = self.attrs.map_attrs(f);
self
}
}
)* }
}

derive_has_attrs_from_field! {
Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm
derive_has_attrs! {
Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
ast::Field, ast::FieldPat, ast::Variant_
}

derive_has_attrs_from_field! { Stmt: .node, ast::Variant: .node.attrs }
69 changes: 64 additions & 5 deletions src/libsyntax/config.rs
Original file line number Diff line number Diff line change
@@ -221,11 +221,21 @@ impl<'a> StripUnconfigured<'a> {
}

pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind {
if let ast::ExprKind::Match(m, arms) = expr_kind {
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
ast::ExprKind::Match(m, arms)
} else {
expr_kind
match expr_kind {
ast::ExprKind::Match(m, arms) => {
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
ast::ExprKind::Match(m, arms)
}
ast::ExprKind::Struct(path, fields, base) => {
let fields = fields.into_iter()
.filter_map(|field| {
self.visit_struct_field_attrs(field.attrs());
self.configure(field)
})
.collect();
ast::ExprKind::Struct(path, fields, base)
}
_ => expr_kind,
}
}

@@ -250,6 +260,51 @@ impl<'a> StripUnconfigured<'a> {
pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> {
self.configure(stmt)
}

pub fn configure_struct_expr_field(&mut self, field: ast::Field) -> Option<ast::Field> {
if !self.features.map(|features| features.struct_field_attributes).unwrap_or(true) {
if !field.attrs.is_empty() {
let mut err = feature_err(&self.sess,
"struct_field_attributes",
field.span,
GateIssue::Language,
"attributes on struct literal fields are unstable");
err.emit();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could refactor out this gated feature check (and the one below) into self.visit_struct_field_attrs() (c.f. self.visit_expr_attrs()) and then refactor away self.configure_struct_expr_field() and self.configure_struct_pat_field()?


self.configure(field)
}

pub fn configure_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
pattern.map(|mut pattern| {
if let ast::PatKind::Struct(path, fields, etc) = pattern.node {
let fields = fields.into_iter()
.filter_map(|field| {
self.visit_struct_field_attrs(field.attrs());
self.configure(field)
})
.collect();
pattern.node = ast::PatKind::Struct(path, fields, etc);
}
pattern
})
}

fn visit_struct_field_attrs(&mut self, attrs: &[ast::Attribute]) {
// flag the offending attributes
for attr in attrs.iter() {
if !self.features.map(|features| features.struct_field_attributes).unwrap_or(true) {
let mut err = feature_err(
&self.sess,
"struct_field_attributes",
attr.span,
GateIssue::Language,
"attributes on struct pattern or literal fields are unstable");
err.emit();
}
}
}
}

impl<'a> fold::Folder for StripUnconfigured<'a> {
@@ -299,6 +354,10 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
// Interpolated AST will get configured once the surrounding tokens are parsed.
mac
}

fn fold_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
fold::noop_fold_pat(self.configure_pat(pattern), self)
}
}

fn is_cfg(attr: &ast::Attribute) -> bool {
8 changes: 7 additions & 1 deletion src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
@@ -699,7 +699,13 @@ 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, is_shorthand: false }
ast::Field {
ident: respan(span, name),
expr: e,
span: span,
is_shorthand: false,
attrs: ast::ThinVec::new(),
}
}
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))
1 change: 1 addition & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
@@ -679,6 +679,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
}

fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
let pat = self.cfg.configure_pat(pat);
match pat.node {
PatKind::Mac(_) => {}
_ => return noop_fold_pat(pat, self),
3 changes: 3 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -327,6 +327,9 @@ declare_features! (

// The `unadjusted` ABI. Perma unstable.
(active, abi_unadjusted, "1.16.0", None),

// Allows attributes on struct literal fields.
(active, struct_field_attributes, "1.16.0", Some(38814)),
);

declare_features! (
2 changes: 2 additions & 0 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
@@ -830,6 +830,7 @@ pub fn noop_fold_field<T: Folder>(f: Field, folder: &mut T) -> Field {
expr: folder.fold_expr(f.expr),
span: folder.new_span(f.span),
is_shorthand: f.is_shorthand,
attrs: fold_thin_attrs(f.attrs, folder),
}
}

@@ -1089,6 +1090,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
ident: folder.fold_ident(f.node.ident),
pat: folder.fold_pat(f.node.pat),
is_shorthand: f.node.is_shorthand,
attrs: fold_attrs(f.node.attrs.into(), folder).into()
}}
});
PatKind::Struct(pth, fs, etc)
13 changes: 10 additions & 3 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
@@ -1946,6 +1946,7 @@ impl<'a> Parser<'a> {

/// Parse ident (COLON expr)?
pub fn parse_field(&mut self) -> PResult<'a, Field> {
let attrs = self.parse_outer_attributes()?;
let lo = self.span.lo;
let hi;

@@ -1968,6 +1969,7 @@ impl<'a> Parser<'a> {
span: mk_sp(lo, expr.span.hi),
expr: expr,
is_shorthand: is_shorthand,
attrs: attrs.into(),
})
}

@@ -3436,6 +3438,7 @@ impl<'a> Parser<'a> {
if self.check(&token::CloseDelim(token::Brace)) { break }
}

let attrs = self.parse_outer_attributes()?;
let lo = self.span.lo;
let hi;

@@ -3493,9 +3496,13 @@ impl<'a> Parser<'a> {
};

fields.push(codemap::Spanned { span: mk_sp(lo, hi),
node: ast::FieldPat { ident: fieldname,
pat: subpat,
is_shorthand: is_shorthand }});
node: ast::FieldPat {
ident: fieldname,
pat: subpat,
is_shorthand: is_shorthand,
attrs: attrs.into(),
}
});
}
return Ok((fields, etc));
}
2 changes: 2 additions & 0 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
@@ -427,6 +427,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
PatKind::Struct(ref path, ref fields, _) => {
visitor.visit_path(path, pattern.id);
for field in fields {
walk_list!(visitor, visit_attribute, field.node.attrs.iter());
visitor.visit_ident(field.span, field.node.ident);
visitor.visit_pat(&field.node.pat)
}
@@ -659,6 +660,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Struct(ref path, ref fields, ref optional_base) => {
visitor.visit_path(path, expression.id);
for field in fields {
walk_list!(visitor, visit_attribute, field.attrs.iter());
Copy link
Contributor

@jseyfried jseyfried Jan 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe field.attrs.iter() can be just field.attrs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot. ThinVec does not implement IntoIterator. &ThinVec also does not, leaving only this approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right -- field.attrs.deref() should work then, or just leave as is.

visitor.visit_ident(field.ident.span, field.ident.node);
visitor.visit_expr(&field.expr)
}
1 change: 1 addition & 0 deletions src/libsyntax_ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
@@ -1550,6 +1550,7 @@ impl<'a> TraitDef<'a> {
ident: ident.unwrap(),
pat: pat,
is_shorthand: false,
attrs: ast::ThinVec::new(),
},
}
})
20 changes: 20 additions & 0 deletions src/test/compile-fail/struct-field-attr-feature-gate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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 {
present: (),
}

fn main() {
let foo = Foo { #[cfg(all())] present: () };
//~^ ERROR attributes on struct pattern or literal fields are unstable
let Foo { #[cfg(all())] present: () } = foo;
//~^ ERROR attributes on struct pattern or literal fields are unstable
}
30 changes: 30 additions & 0 deletions src/test/compile-fail/struct-field-cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2017 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(struct_field_attributes)]

struct Foo {
present: (),
}

fn main() {
let foo = Foo { #[cfg(all())] present: () };
let _ = Foo { #[cfg(any())] present: () };
//~^ ERROR missing field `present` in initializer of `Foo`
let _ = Foo { present: (), #[cfg(any())] absent: () };
let _ = Foo { present: (), #[cfg(all())] absent: () };
//~^ ERROR struct `Foo` has no field named `absent`
let Foo { #[cfg(all())] present: () } = foo;
let Foo { #[cfg(any())] present: () } = foo;
//~^ ERROR pattern does not mention field `present`
let Foo { present: (), #[cfg(any())] absent: () } = foo;
let Foo { present: (), #[cfg(all())] absent: () } = foo;
//~^ ERROR struct `Foo` does not have a field named `absent`
}