about summary refs log tree commit diff
path: root/src/libsyntax/parse/parser/generics.rs
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-08-11 20:44:09 +0200
committerMazdak Farrokhzad <twingoow@gmail.com>2019-08-11 20:44:09 +0200
commit385d07f359cd4d4237cac6ae030d07792750a506 (patch)
treed5b2186abd4778d978997b7b9f5b241a9074ad57 /src/libsyntax/parse/parser/generics.rs
parentd6d93b3d829116dbcd0c27d746a5ffd46f703ede (diff)
downloadrust-385d07f359cd4d4237cac6ae030d07792750a506.tar.gz
rust-385d07f359cd4d4237cac6ae030d07792750a506.zip
parser: move into generics.rs
Diffstat (limited to 'src/libsyntax/parse/parser/generics.rs')
-rw-r--r--src/libsyntax/parse/parser/generics.rs276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/libsyntax/parse/parser/generics.rs b/src/libsyntax/parse/parser/generics.rs
new file mode 100644
index 00000000000..54f24f8ef2b
--- /dev/null
+++ b/src/libsyntax/parse/parser/generics.rs
@@ -0,0 +1,276 @@
+use super::{Parser, PResult};
+
+use crate::ast::{self, WhereClause, GenericParam, GenericParamKind, GenericBounds, Attribute};
+use crate::parse::token;
+use crate::source_map::DUMMY_SP;
+use crate::symbol::kw;
+
+impl<'a> Parser<'a> {
+    /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
+    ///
+    /// ```
+    /// BOUND = LT_BOUND (e.g., `'a`)
+    /// ```
+    fn parse_lt_param_bounds(&mut self) -> GenericBounds {
+        let mut lifetimes = Vec::new();
+        while self.check_lifetime() {
+            lifetimes.push(ast::GenericBound::Outlives(self.expect_lifetime()));
+
+            if !self.eat_plus() {
+                break
+            }
+        }
+        lifetimes
+    }
+
+    /// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`.
+    fn parse_ty_param(&mut self,
+                      preceding_attrs: Vec<Attribute>)
+                      -> PResult<'a, GenericParam> {
+        let ident = self.parse_ident()?;
+
+        // Parse optional colon and param bounds.
+        let bounds = if self.eat(&token::Colon) {
+            self.parse_generic_bounds(Some(self.prev_span))?
+        } else {
+            Vec::new()
+        };
+
+        let default = if self.eat(&token::Eq) {
+            Some(self.parse_ty()?)
+        } else {
+            None
+        };
+
+        Ok(GenericParam {
+            ident,
+            id: ast::DUMMY_NODE_ID,
+            attrs: preceding_attrs.into(),
+            bounds,
+            kind: GenericParamKind::Type {
+                default,
+            }
+        })
+    }
+
+    fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
+        self.expect_keyword(kw::Const)?;
+        let ident = self.parse_ident()?;
+        self.expect(&token::Colon)?;
+        let ty = self.parse_ty()?;
+
+        Ok(GenericParam {
+            ident,
+            id: ast::DUMMY_NODE_ID,
+            attrs: preceding_attrs.into(),
+            bounds: Vec::new(),
+            kind: GenericParamKind::Const {
+                ty,
+            }
+        })
+    }
+
+    /// Parses a (possibly empty) list of lifetime and type parameters, possibly including
+    /// a trailing comma and erroneous trailing attributes.
+    crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
+        let mut params = Vec::new();
+        loop {
+            let attrs = self.parse_outer_attributes()?;
+            if self.check_lifetime() {
+                let lifetime = self.expect_lifetime();
+                // Parse lifetime parameter.
+                let bounds = if self.eat(&token::Colon) {
+                    self.parse_lt_param_bounds()
+                } else {
+                    Vec::new()
+                };
+                params.push(ast::GenericParam {
+                    ident: lifetime.ident,
+                    id: lifetime.id,
+                    attrs: attrs.into(),
+                    bounds,
+                    kind: ast::GenericParamKind::Lifetime,
+                });
+            } else if self.check_keyword(kw::Const) {
+                // Parse const parameter.
+                params.push(self.parse_const_param(attrs)?);
+            } else if self.check_ident() {
+                // Parse type parameter.
+                params.push(self.parse_ty_param(attrs)?);
+            } else {
+                // Check for trailing attributes and stop parsing.
+                if !attrs.is_empty() {
+                    if !params.is_empty() {
+                        self.struct_span_err(
+                            attrs[0].span,
+                            &format!("trailing attribute after generic parameter"),
+                        )
+                        .span_label(attrs[0].span, "attributes must go before parameters")
+                        .emit();
+                    } else {
+                        self.struct_span_err(
+                            attrs[0].span,
+                            &format!("attribute without generic parameters"),
+                        )
+                        .span_label(
+                            attrs[0].span,
+                            "attributes are only permitted when preceding parameters",
+                        )
+                        .emit();
+                    }
+                }
+                break
+            }
+
+            if !self.eat(&token::Comma) {
+                break
+            }
+        }
+        Ok(params)
+    }
+
+    /// Parses a set of optional generic type parameter declarations. Where
+    /// clauses are not parsed here, and must be added later via
+    /// `parse_where_clause()`.
+    ///
+    /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > )
+    ///                  | ( < lifetimes , typaramseq ( , )? > )
+    /// where   typaramseq = ( typaram ) | ( typaram , typaramseq )
+    pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
+        let span_lo = self.token.span;
+        let (params, span) = if self.eat_lt() {
+            let params = self.parse_generic_params()?;
+            self.expect_gt()?;
+            (params, span_lo.to(self.prev_span))
+        } else {
+            (vec![], self.prev_span.between(self.token.span))
+        };
+        Ok(ast::Generics {
+            params,
+            where_clause: WhereClause {
+                predicates: Vec::new(),
+                span: DUMMY_SP,
+            },
+            span,
+        })
+    }
+
+    /// Parses an optional where-clause and places it in `generics`.
+    ///
+    /// ```ignore (only-for-syntax-highlight)
+    /// where T : Trait<U, V> + 'b, 'a : 'b
+    /// ```
+    pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
+        let mut where_clause = WhereClause {
+            predicates: Vec::new(),
+            span: self.prev_span.to(self.prev_span),
+        };
+
+        if !self.eat_keyword(kw::Where) {
+            return Ok(where_clause);
+        }
+        let lo = self.prev_span;
+
+        // We are considering adding generics to the `where` keyword as an alternative higher-rank
+        // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
+        // change we parse those generics now, but report an error.
+        if self.choose_generics_over_qpath() {
+            let generics = self.parse_generics()?;
+            self.struct_span_err(
+                generics.span,
+                "generic parameters on `where` clauses are reserved for future use",
+            )
+                .span_label(generics.span, "currently unsupported")
+                .emit();
+        }
+
+        loop {
+            let lo = self.token.span;
+            if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
+                let lifetime = self.expect_lifetime();
+                // Bounds starting with a colon are mandatory, but possibly empty.
+                self.expect(&token::Colon)?;
+                let bounds = self.parse_lt_param_bounds();
+                where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
+                    ast::WhereRegionPredicate {
+                        span: lo.to(self.prev_span),
+                        lifetime,
+                        bounds,
+                    }
+                ));
+            } else if self.check_type() {
+                // Parse optional `for<'a, 'b>`.
+                // This `for` is parsed greedily and applies to the whole predicate,
+                // the bounded type can have its own `for` applying only to it.
+                // Examples:
+                // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
+                // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
+                // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
+                let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+
+                // Parse type with mandatory colon and (possibly empty) bounds,
+                // or with mandatory equality sign and the second type.
+                let ty = self.parse_ty()?;
+                if self.eat(&token::Colon) {
+                    let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
+                    where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
+                        ast::WhereBoundPredicate {
+                            span: lo.to(self.prev_span),
+                            bound_generic_params: lifetime_defs,
+                            bounded_ty: ty,
+                            bounds,
+                        }
+                    ));
+                // FIXME: Decide what should be used here, `=` or `==`.
+                // FIXME: We are just dropping the binders in lifetime_defs on the floor here.
+                } else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
+                    let rhs_ty = self.parse_ty()?;
+                    where_clause.predicates.push(ast::WherePredicate::EqPredicate(
+                        ast::WhereEqPredicate {
+                            span: lo.to(self.prev_span),
+                            lhs_ty: ty,
+                            rhs_ty,
+                            id: ast::DUMMY_NODE_ID,
+                        }
+                    ));
+                } else {
+                    return self.unexpected();
+                }
+            } else {
+                break
+            }
+
+            if !self.eat(&token::Comma) {
+                break
+            }
+        }
+
+        where_clause.span = lo.to(self.prev_span);
+        Ok(where_clause)
+    }
+
+    pub(super) fn choose_generics_over_qpath(&self) -> bool {
+        // There's an ambiguity between generic parameters and qualified paths in impls.
+        // If we see `<` it may start both, so we have to inspect some following tokens.
+        // The following combinations can only start generics,
+        // but not qualified paths (with one exception):
+        //     `<` `>` - empty generic parameters
+        //     `<` `#` - generic parameters with attributes
+        //     `<` (LIFETIME|IDENT) `>` - single generic parameter
+        //     `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
+        //     `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
+        //     `<` (LIFETIME|IDENT) `=` - generic parameter with a default
+        //     `<` const                - generic const parameter
+        // The only truly ambiguous case is
+        //     `<` IDENT `>` `::` IDENT ...
+        // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
+        // because this is what almost always expected in practice, qualified paths in impls
+        // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
+        self.token == token::Lt &&
+            (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
+             self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
+                self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
+                                       t == &token::Colon || t == &token::Eq) ||
+            self.is_keyword_ahead(1, &[kw::Const]))
+    }
+}