]> git.lizzy.rs Git - rust.git/commitdiff
Add attribute support to generic lifetime and type parameters.
authorFelix S. Klock II <pnkfelix@pnkfx.org>
Tue, 17 May 2016 16:51:45 +0000 (18:51 +0200)
committerFelix S. Klock II <pnkfelix@pnkfx.org>
Fri, 23 Sep 2016 15:01:04 +0000 (17:01 +0200)
I am using `ThinAttributes` rather than a vector for attributes
attached to generics, since I expect almost all lifetime and types
parameters to not carry any attributes.

src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/feature_gate.rs
src/libsyntax/fold.rs
src/libsyntax/parse/parser.rs
src/libsyntax/visit.rs
src/libsyntax_ext/deriving/generic/mod.rs
src/libsyntax_ext/deriving/generic/ty.rs

index c18b36161dfcf3a190d3c3e4b71324912d5828d8..c5765f159a9cff6d2a822309f804cd57206af1bd 100644 (file)
@@ -121,6 +121,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 /// A lifetime definition, e.g. `'a: 'b+'c+'d`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct LifetimeDef {
+    pub attrs: ThinVec<Attribute>,
     pub lifetime: Lifetime,
     pub bounds: Vec<Lifetime>
 }
@@ -370,6 +371,7 @@ pub enum TraitBoundModifier {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct TyParam {
+    pub attrs: ThinVec<Attribute>,
     pub ident: Ident,
     pub id: NodeId,
     pub bounds: TyParamBounds,
index b81d95a6998c310e01006df4c6733d0f81d28684..b822599e941f1ad10bdd7ccae30ea8989c7273f2 100644 (file)
@@ -73,6 +73,7 @@ fn ty_ptr(&self, span: Span,
     fn typaram(&self,
                span: Span,
                id: ast::Ident,
+               attrs: Vec<ast::Attribute>,
                bounds: ast::TyParamBounds,
                default: Option<P<ast::Ty>>) -> ast::TyParam;
 
@@ -83,6 +84,7 @@ fn typaram(&self,
     fn lifetime_def(&self,
                     span: Span,
                     name: ast::Name,
+                    attrs: Vec<ast::Attribute>,
                     bounds: Vec<ast::Lifetime>)
                     -> ast::LifetimeDef;
 
@@ -452,11 +454,13 @@ fn ty_infer(&self, span: Span) -> P<ast::Ty> {
     fn typaram(&self,
                span: Span,
                id: ast::Ident,
+               attrs: Vec<ast::Attribute>,
                bounds: ast::TyParamBounds,
                default: Option<P<ast::Ty>>) -> ast::TyParam {
         ast::TyParam {
             ident: id,
             id: ast::DUMMY_NODE_ID,
+            attrs: attrs.into(),
             bounds: bounds,
             default: default,
             span: span
@@ -503,9 +507,11 @@ fn lifetime(&self, span: Span, name: ast::Name) -> ast::Lifetime {
     fn lifetime_def(&self,
                     span: Span,
                     name: ast::Name,
+                    attrs: Vec<ast::Attribute>,
                     bounds: Vec<ast::Lifetime>)
                     -> ast::LifetimeDef {
         ast::LifetimeDef {
+            attrs: attrs.into(),
             lifetime: self.lifetime(span, name),
             bounds: bounds
         }
index 27b97a0ad665bcc58657ba0168edb8719c1642e9..67372351b44d96b29171ec81c569759e62ac5d84 100644 (file)
@@ -302,6 +302,9 @@ pub fn new() -> Features {
     // Used to identify the `compiler_builtins` crate
     // rustc internal
     (active, compiler_builtins, "1.13.0", None),
+
+    // Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
+    (active, generic_param_attrs, "1.11.0", Some(34761)),
 );
 
 declare_features! (
@@ -1208,6 +1211,24 @@ fn visit_vis(&mut self, vis: &ast::Visibility) {
 
         visit::walk_vis(self, vis)
     }
+
+    fn visit_generics(&mut self, g: &ast::Generics) {
+        for t in &g.ty_params {
+            if !t.attrs.is_empty() {
+                gate_feature_post!(&self, generic_param_attrs, t.attrs[0].span,
+                                   "attributes on type parameter bindings are experimental");
+            }
+        }
+        visit::walk_generics(self, g)
+    }
+
+    fn visit_lifetime_def(&mut self, lifetime_def: &ast::LifetimeDef) {
+        if !lifetime_def.attrs.is_empty() {
+            gate_feature_post!(&self, generic_param_attrs, lifetime_def.attrs[0].span,
+                               "attributes on lifetime bindings are experimental");
+        }
+        visit::walk_lifetime_def(self, lifetime_def)
+    }
 }
 
 pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
index 36f273e1dbc299f3c6c31773578ee050307f6649..e28c67602c532a28bf6166fccbab19437fe7536e 100644 (file)
@@ -662,8 +662,13 @@ pub fn noop_fold_ty_param_bound<T>(tpb: TyParamBound, fld: &mut T)
 }
 
 pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
-    let TyParam {id, ident, bounds, default, span} = tp;
+    let TyParam {attrs, id, ident, bounds, default, span} = tp;
+    let attrs: Vec<_> = attrs.into();
     TyParam {
+        attrs: attrs.into_iter()
+            .flat_map(|x| fld.fold_attribute(x).into_iter())
+            .collect::<Vec<_>>()
+            .into(),
         id: fld.new_id(id),
         ident: ident,
         bounds: fld.fold_bounds(bounds),
@@ -687,7 +692,12 @@ pub fn noop_fold_lifetime<T: Folder>(l: Lifetime, fld: &mut T) -> Lifetime {
 
 pub fn noop_fold_lifetime_def<T: Folder>(l: LifetimeDef, fld: &mut T)
                                          -> LifetimeDef {
+    let attrs: Vec<_> = l.attrs.into();
     LifetimeDef {
+        attrs: attrs.into_iter()
+            .flat_map(|x| fld.fold_attribute(x).into_iter())
+            .collect::<Vec<_>>()
+            .into(),
         lifetime: fld.fold_lifetime(l.lifetime),
         bounds: fld.fold_lifetimes(l.bounds),
     }
index 23085fadc5e6068c3c32bb217fd05a655a08da32..fe5ff5c47b24895bc5bcd69f60271b35b56ab032 100644 (file)
@@ -1184,7 +1184,7 @@ pub fn parse_trait_item(&mut self) -> PResult<'a, TraitItem> {
         let lo = self.span.lo;
 
         let (name, node) = if self.eat_keyword(keywords::Type) {
-            let TyParam {ident, bounds, default, ..} = self.parse_ty_param()?;
+            let TyParam {ident, bounds, default, ..} = self.parse_ty_param(vec![])?;
             self.expect(&token::Semi)?;
             (ident, TraitItemKind::Type(bounds, default))
         } else if self.is_const_item() {
@@ -1923,10 +1923,22 @@ pub fn parse_lifetime(&mut self) -> PResult<'a, ast::Lifetime> {
 
     /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def  =
     /// lifetime [':' lifetimes]`
-    pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
-
+    ///
+    /// If `followed_by_ty_params` is None, then we are in a context
+    /// where only lifetime parameters are allowed, and thus we should
+    /// error if we encounter attributes after the bound lifetimes.
+    ///
+    /// If `followed_by_ty_params` is Some(r), then there may be type
+    /// parameter bindings after the lifetimes, so we should pass
+    /// along the parsed attributes to be attached to the first such
+    /// type parmeter.
+    pub fn parse_lifetime_defs(&mut self,
+                               followed_by_ty_params: Option<&mut Vec<ast::Attribute>>)
+                               -> PResult<'a, Vec<ast::LifetimeDef>>
+    {
         let mut res = Vec::new();
         loop {
+            let attrs = self.parse_outer_attributes()?;
             match self.token {
                 token::Lifetime(_) => {
                     let lifetime = self.parse_lifetime()?;
@@ -1936,11 +1948,20 @@ pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
                         } else {
                             Vec::new()
                         };
-                    res.push(ast::LifetimeDef { lifetime: lifetime,
+                    res.push(ast::LifetimeDef { attrs: attrs.into(),
+                                                lifetime: lifetime,
                                                 bounds: bounds });
                 }
 
                 _ => {
+                    if let Some(recv) = followed_by_ty_params {
+                        assert!(recv.is_empty());
+                        *recv = attrs;
+                    } else {
+                        let msg = "encountered trailing attributes after lifetime parameters";
+                        return Err(self.fatal(msg));
+                    }
+                    debug!("parse_lifetime_defs ret {:?}", res);
                     return Ok(res);
                 }
             }
@@ -4238,7 +4259,7 @@ fn parse_ty_param_bounds(&mut self,
     }
 
     /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
-    fn parse_ty_param(&mut self) -> PResult<'a, TyParam> {
+    fn parse_ty_param(&mut self, preceding_attrs: Vec<ast::Attribute>) -> PResult<'a, TyParam> {
         let span = self.span;
         let ident = self.parse_ident()?;
 
@@ -4252,6 +4273,7 @@ fn parse_ty_param(&mut self) -> PResult<'a, TyParam> {
         };
 
         Ok(TyParam {
+            attrs: preceding_attrs.into(),
             ident: ident,
             id: ast::DUMMY_NODE_ID,
             bounds: bounds,
@@ -4272,11 +4294,18 @@ pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
         let span_lo = self.span.lo;
 
         if self.eat(&token::Lt) {
-            let lifetime_defs = self.parse_lifetime_defs()?;
+            let mut attrs = vec![];
+            let lifetime_defs = self.parse_lifetime_defs(Some(&mut attrs))?;
             let mut seen_default = false;
+            let mut post_lifetime_attrs = Some(attrs);
             let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| {
                 p.forbid_lifetime()?;
-                let ty_param = p.parse_ty_param()?;
+                let attrs = match post_lifetime_attrs.as_mut() {
+                    None => p.parse_outer_attributes()?,
+                    Some(attrs) => mem::replace(attrs, vec![]),
+                };
+                post_lifetime_attrs = None;
+                let ty_param = p.parse_ty_param(attrs)?;
                 if ty_param.default.is_some() {
                     seen_default = true;
                 } else if seen_default {
@@ -4433,7 +4462,7 @@ pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> {
                     let bound_lifetimes = if self.eat_keyword(keywords::For) {
                         // Higher ranked constraint.
                         self.expect(&token::Lt)?;
-                        let lifetime_defs = self.parse_lifetime_defs()?;
+                        let lifetime_defs = self.parse_lifetime_defs(None)?;
                         self.expect_gt()?;
                         lifetime_defs
                     } else {
@@ -5006,7 +5035,7 @@ fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> {
     fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
         if self.eat_keyword(keywords::For) {
             self.expect(&token::Lt)?;
-            let lifetime_defs = self.parse_lifetime_defs()?;
+            let lifetime_defs = self.parse_lifetime_defs(None)?;
             self.expect_gt()?;
             Ok(lifetime_defs)
         } else {
index 57b06c40878fe9dcea3adc3c7486a9a0f4c6a018..70864c3a6e67496659ad5287f54c58017f808637 100644 (file)
@@ -201,6 +201,7 @@ pub fn walk_lifetime<V: Visitor>(visitor: &mut V, lifetime: &Lifetime) {
 pub fn walk_lifetime_def<V: Visitor>(visitor: &mut V, lifetime_def: &LifetimeDef) {
     visitor.visit_lifetime(&lifetime_def.lifetime);
     walk_list!(visitor, visit_lifetime, &lifetime_def.bounds);
+    walk_list!(visitor, visit_attribute, &*lifetime_def.attrs);
 }
 
 pub fn walk_poly_trait_ref<V>(visitor: &mut V, trait_ref: &PolyTraitRef, _: &TraitBoundModifier)
@@ -474,6 +475,7 @@ pub fn walk_generics<V: Visitor>(visitor: &mut V, generics: &Generics) {
         visitor.visit_ident(param.span, param.ident);
         walk_list!(visitor, visit_ty_param_bound, &param.bounds);
         walk_list!(visitor, visit_ty, &param.default);
+        walk_list!(visitor, visit_attribute, &*param.attrs);
     }
     walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
     for predicate in &generics.where_clause.predicates {
index e307925a6ed83907b03de79f5b25d2b13188b4f2..bc47d8f4e613741977ac0dc0a52c3b78acd40aed 100644 (file)
@@ -536,7 +536,7 @@ fn create_derived_impl(&self,
                 bounds.push((*declared_bound).clone());
             }
 
-            cx.typaram(self.span, ty_param.ident, P::from_vec(bounds), None)
+            cx.typaram(self.span, ty_param.ident, vec![], P::from_vec(bounds), None)
         }));
 
         // and similarly for where clauses
index 210878b7c9f0e09d3d62d1052cc967514bcece27..4749d082bc0ec3dcd0723e57a000862ed3e866f7 100644 (file)
@@ -194,6 +194,7 @@ pub fn to_path(&self,
 fn mk_ty_param(cx: &ExtCtxt,
                span: Span,
                name: &str,
+               attrs: &[ast::Attribute],
                bounds: &[Path],
                self_ident: Ident,
                self_generics: &Generics)
@@ -204,7 +205,7 @@ fn mk_ty_param(cx: &ExtCtxt,
             cx.typarambound(path)
         })
         .collect();
-    cx.typaram(span, cx.ident_of(name), bounds, None)
+    cx.typaram(span, cx.ident_of(name), attrs.to_owned(), bounds, None)
 }
 
 fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>, span: Span)
@@ -246,7 +247,7 @@ pub fn to_generics(&self,
                 let bounds = bounds.iter()
                     .map(|b| cx.lifetime(span, cx.ident_of(*b).name))
                     .collect();
-                cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
+                cx.lifetime_def(span, cx.ident_of(*lt).name, vec![], bounds)
             })
             .collect();
         let ty_params = self.bounds
@@ -254,7 +255,7 @@ pub fn to_generics(&self,
             .map(|t| {
                 match *t {
                     (ref name, ref bounds) => {
-                        mk_ty_param(cx, span, *name, bounds, self_ty, self_generics)
+                        mk_ty_param(cx, span, *name, &[], bounds, self_ty, self_generics)
                     }
                 }
             })