Refactor ast::GenericParam as a struct
This commit is contained in:
parent
fba1fe2108
commit
2c6ff2469a
16 changed files with 337 additions and 354 deletions
|
@ -759,20 +759,20 @@ impl<'a> LoweringContext<'a> {
|
||||||
hir_name
|
hir_name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluates `f` with the lifetimes in `lt_defs` in-scope.
|
// Evaluates `f` with the lifetimes in `params` in-scope.
|
||||||
// This is used to track which lifetimes have already been defined, and
|
// This is used to track which lifetimes have already been defined, and
|
||||||
// which are new in-band lifetimes that need to have a definition created
|
// which are new in-band lifetimes that need to have a definition created
|
||||||
// for them.
|
// for them.
|
||||||
fn with_in_scope_lifetime_defs<'l, T, F>(
|
fn with_in_scope_lifetime_defs<'l, T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
lt_defs: impl Iterator<Item = &'l LifetimeDef>,
|
params: impl Iterator<Item = &'l GenericParamAST>,
|
||||||
f: F,
|
f: F,
|
||||||
) -> T
|
) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut LoweringContext) -> T,
|
F: FnOnce(&mut LoweringContext) -> T,
|
||||||
{
|
{
|
||||||
let old_len = self.in_scope_lifetimes.len();
|
let old_len = self.in_scope_lifetimes.len();
|
||||||
let lt_def_names = lt_defs.map(|lt_def| lt_def.lifetime.ident.name);
|
let lt_def_names = params.map(|param| param.ident.name);
|
||||||
self.in_scope_lifetimes.extend(lt_def_names);
|
self.in_scope_lifetimes.extend(lt_def_names);
|
||||||
|
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
|
@ -781,8 +781,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as the method above, but accepts `hir::LifetimeDef`s
|
// Same as the method above, but accepts `hir::GenericParam`s
|
||||||
// instead of `ast::LifetimeDef`s.
|
// instead of `ast::GenericParam`s.
|
||||||
// This should only be used with generics that have already had their
|
// This should only be used with generics that have already had their
|
||||||
// in-band lifetimes added. In practice, this means that this function is
|
// in-band lifetimes added. In practice, this means that this function is
|
||||||
// only used when lowering a child item of a trait or impl.
|
// only used when lowering a child item of a trait or impl.
|
||||||
|
@ -817,8 +817,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
F: FnOnce(&mut LoweringContext) -> T,
|
F: FnOnce(&mut LoweringContext) -> T,
|
||||||
{
|
{
|
||||||
let (in_band_defs, (mut lowered_generics, res)) = self.with_in_scope_lifetime_defs(
|
let (in_band_defs, (mut lowered_generics, res)) = self.with_in_scope_lifetime_defs(
|
||||||
generics.params.iter().filter_map(|p| match p {
|
generics.params.iter().filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ld) => Some(ld),
|
GenericParamKindAST::Lifetime { .. } => Some(param),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
|this| {
|
|this| {
|
||||||
|
@ -1076,8 +1076,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
hir::TyRptr(lifetime, self.lower_mt(mt, itctx))
|
hir::TyRptr(lifetime, self.lower_mt(mt, itctx))
|
||||||
}
|
}
|
||||||
TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(
|
TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(
|
||||||
f.generic_params.iter().filter_map(|p| match p {
|
f.generic_params.iter().filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ld) => Some(ld),
|
GenericParamKindAST::Lifetime { .. } => Some(param),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
|this| {
|
|this| {
|
||||||
|
@ -1940,21 +1940,19 @@ impl<'a> LoweringContext<'a> {
|
||||||
add_bounds: &NodeMap<Vec<TyParamBound>>,
|
add_bounds: &NodeMap<Vec<TyParamBound>>,
|
||||||
itctx: ImplTraitContext)
|
itctx: ImplTraitContext)
|
||||||
-> hir::GenericParam {
|
-> hir::GenericParam {
|
||||||
match param {
|
match param.kind {
|
||||||
GenericParamAST::Lifetime(ref lifetime_def) => {
|
GenericParamKindAST::Lifetime { ref bounds, ref lifetime, .. } => {
|
||||||
let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
|
let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
|
||||||
self.is_collecting_in_band_lifetimes = false;
|
self.is_collecting_in_band_lifetimes = false;
|
||||||
|
|
||||||
let lifetime = self.lower_lifetime(&lifetime_def.lifetime);
|
let lifetime = self.lower_lifetime(lifetime);
|
||||||
let param = hir::GenericParam {
|
let param = hir::GenericParam {
|
||||||
id: lifetime.id,
|
id: lifetime.id,
|
||||||
span: lifetime.span,
|
span: lifetime.span,
|
||||||
pure_wrt_drop: attr::contains_name(&lifetime_def.attrs, "may_dangle"),
|
pure_wrt_drop: attr::contains_name(¶m.attrs, "may_dangle"),
|
||||||
kind: hir::GenericParamKind::Lifetime {
|
kind: hir::GenericParamKind::Lifetime {
|
||||||
name: lifetime.name,
|
name: lifetime.name,
|
||||||
bounds: lifetime_def.bounds
|
bounds: bounds.iter().map(|lt| self.lower_lifetime(lt)).collect(),
|
||||||
.iter()
|
|
||||||
.map(|lt| self.lower_lifetime(lt)).collect(),
|
|
||||||
in_band: false,
|
in_band: false,
|
||||||
lifetime_deprecated: lifetime,
|
lifetime_deprecated: lifetime,
|
||||||
}
|
}
|
||||||
|
@ -1964,8 +1962,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
|
|
||||||
param
|
param
|
||||||
}
|
}
|
||||||
GenericParamAST::Type(ref ty_param) => {
|
GenericParamKindAST::Type { ref bounds, ref default } => {
|
||||||
let mut name = self.lower_ident(ty_param.ident);
|
let mut name = self.lower_ident(param.ident);
|
||||||
|
|
||||||
// Don't expose `Self` (recovered "keyword used as ident" parse error).
|
// Don't expose `Self` (recovered "keyword used as ident" parse error).
|
||||||
// `rustc::ty` expects `Self` to be only used for a trait's `Self`.
|
// `rustc::ty` expects `Self` to be only used for a trait's `Self`.
|
||||||
|
@ -1974,8 +1972,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
name = Symbol::gensym("Self");
|
name = Symbol::gensym("Self");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bounds = self.lower_bounds(&ty_param.bounds, itctx);
|
let mut bounds = self.lower_bounds(bounds, itctx);
|
||||||
let add_bounds = add_bounds.get(&ty_param.id).map_or(&[][..], |x| &x);
|
let add_bounds = add_bounds.get(¶m.id).map_or(&[][..], |x| &x);
|
||||||
if !add_bounds.is_empty() {
|
if !add_bounds.is_empty() {
|
||||||
bounds = bounds
|
bounds = bounds
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1984,22 +1982,20 @@ impl<'a> LoweringContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::GenericParam {
|
hir::GenericParam {
|
||||||
id: self.lower_node_id(ty_param.id).node_id,
|
id: self.lower_node_id(param.id).node_id,
|
||||||
span: ty_param.ident.span,
|
span: param.ident.span,
|
||||||
pure_wrt_drop: attr::contains_name(&ty_param.attrs, "may_dangle"),
|
pure_wrt_drop: attr::contains_name(¶m.attrs, "may_dangle"),
|
||||||
kind: hir::GenericParamKind::Type {
|
kind: hir::GenericParamKind::Type {
|
||||||
name,
|
name,
|
||||||
bounds,
|
bounds,
|
||||||
default: ty_param.default.as_ref()
|
default: default.as_ref().map(|x| {
|
||||||
.map(|x| {
|
self.lower_ty(x, ImplTraitContext::Disallowed)
|
||||||
self.lower_ty(x, ImplTraitContext::Disallowed)
|
}),
|
||||||
}),
|
synthetic: param.attrs.iter()
|
||||||
synthetic: ty_param.attrs
|
.filter(|attr| attr.check_name("rustc_synthetic"))
|
||||||
.iter()
|
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
|
||||||
.filter(|attr| attr.check_name("rustc_synthetic"))
|
.nth(0),
|
||||||
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
|
attrs: self.lower_attrs(¶m.attrs),
|
||||||
.nth(0),
|
|
||||||
attrs: self.lower_attrs(&ty_param.attrs),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2015,13 +2011,18 @@ impl<'a> LoweringContext<'a> {
|
||||||
params.iter().map(|param| self.lower_generic_param(param, add_bounds, itctx)).collect()
|
params.iter().map(|param| self.lower_generic_param(param, add_bounds, itctx)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_generics(&mut self, g: &Generics, itctx: ImplTraitContext) -> hir::Generics {
|
fn lower_generics(
|
||||||
|
&mut self,
|
||||||
|
generics: &Generics,
|
||||||
|
itctx: ImplTraitContext)
|
||||||
|
-> hir::Generics
|
||||||
|
{
|
||||||
// Collect `?Trait` bounds in where clause and move them to parameter definitions.
|
// Collect `?Trait` bounds in where clause and move them to parameter definitions.
|
||||||
// FIXME: This could probably be done with less rightward drift. Also looks like two control
|
// FIXME: This could probably be done with less rightward drift. Also looks like two control
|
||||||
// paths where report_error is called are also the only paths that advance to after
|
// paths where report_error is called are also the only paths that advance to after
|
||||||
// the match statement, so the error reporting could probably just be moved there.
|
// the match statement, so the error reporting could probably just be moved there.
|
||||||
let mut add_bounds = NodeMap();
|
let mut add_bounds = NodeMap();
|
||||||
for pred in &g.where_clause.predicates {
|
for pred in &generics.where_clause.predicates {
|
||||||
if let WherePredicate::BoundPredicate(ref bound_pred) = *pred {
|
if let WherePredicate::BoundPredicate(ref bound_pred) = *pred {
|
||||||
'next_bound: for bound in &bound_pred.bounds {
|
'next_bound: for bound in &bound_pred.bounds {
|
||||||
if let TraitTyParamBound(_, TraitBoundModifier::Maybe) = *bound {
|
if let TraitTyParamBound(_, TraitBoundModifier::Maybe) = *bound {
|
||||||
|
@ -2045,15 +2046,17 @@ impl<'a> LoweringContext<'a> {
|
||||||
if let Some(node_id) =
|
if let Some(node_id) =
|
||||||
self.resolver.definitions().as_local_node_id(def_id)
|
self.resolver.definitions().as_local_node_id(def_id)
|
||||||
{
|
{
|
||||||
for param in &g.params {
|
for param in &generics.params {
|
||||||
if let GenericParamAST::Type(ref ty_param) = *param {
|
match param.kind {
|
||||||
if node_id == ty_param.id {
|
GenericParamKindAST::Type { .. } => {
|
||||||
add_bounds
|
if node_id == param.id {
|
||||||
.entry(ty_param.id)
|
add_bounds.entry(param.id)
|
||||||
.or_insert(Vec::new())
|
.or_insert(Vec::new())
|
||||||
.push(bound.clone());
|
.push(bound.clone());
|
||||||
continue 'next_bound;
|
continue 'next_bound;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2068,9 +2071,9 @@ impl<'a> LoweringContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::Generics {
|
hir::Generics {
|
||||||
params: self.lower_generic_params(&g.params, &add_bounds, itctx),
|
params: self.lower_generic_params(&generics.params, &add_bounds, itctx),
|
||||||
where_clause: self.lower_where_clause(&g.where_clause),
|
where_clause: self.lower_where_clause(&generics.where_clause),
|
||||||
span: g.span,
|
span: generics.span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2093,8 +2096,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
span,
|
span,
|
||||||
}) => {
|
}) => {
|
||||||
self.with_in_scope_lifetime_defs(
|
self.with_in_scope_lifetime_defs(
|
||||||
bound_generic_params.iter().filter_map(|p| match p {
|
bound_generic_params.iter().filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ld) => Some(ld),
|
GenericParamKindAST::Lifetime { .. } => Some(param),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
|this| {
|
|this| {
|
||||||
|
@ -2412,8 +2415,8 @@ impl<'a> LoweringContext<'a> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let new_impl_items = self.with_in_scope_lifetime_defs(
|
let new_impl_items = self.with_in_scope_lifetime_defs(
|
||||||
ast_generics.params.iter().filter_map(|p| match p {
|
ast_generics.params.iter().filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ld) => Some(ld),
|
GenericParamKindAST::Lifetime { .. } => Some(param),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
|this| {
|
|this| {
|
||||||
|
|
|
@ -171,24 +171,17 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_generic_param(&mut self, param: &'a GenericParamAST) {
|
fn visit_generic_param(&mut self, param: &'a GenericParamAST) {
|
||||||
match *param {
|
let name = param.ident.name.as_interned_str();
|
||||||
GenericParamAST::Lifetime(ref lifetime_def) => {
|
let def_path_data = match param.kind {
|
||||||
self.create_def(
|
GenericParamKindAST::Lifetime { .. } => DefPathData::LifetimeDef(name),
|
||||||
lifetime_def.lifetime.id,
|
GenericParamKindAST::Type { .. } => DefPathData::TypeParam(name),
|
||||||
DefPathData::LifetimeDef(lifetime_def.lifetime.ident.name.as_interned_str()),
|
};
|
||||||
REGULAR_SPACE,
|
self.create_def(
|
||||||
lifetime_def.lifetime.ident.span
|
param.id,
|
||||||
);
|
def_path_data,
|
||||||
}
|
REGULAR_SPACE,
|
||||||
GenericParamAST::Type(ref ty_param) => {
|
param.ident.span
|
||||||
self.create_def(
|
);
|
||||||
ty_param.id,
|
|
||||||
DefPathData::TypeParam(ty_param.ident.name.as_interned_str()),
|
|
||||||
REGULAR_SPACE,
|
|
||||||
ty_param.ident.span
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit::walk_generic_param(self, param);
|
visit::walk_generic_param(self, param);
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,30 +139,24 @@ impl<'a> AstValidator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_late_bound_lifetime_defs(&self, params: &Vec<GenericParamAST>) {
|
fn check_late_bound_lifetime_defs(&self, params: &Vec<GenericParamAST>) {
|
||||||
// Check: Only lifetime parameters
|
// Check only lifetime parameters are present and that the lifetime
|
||||||
let non_lifetime_param_spans : Vec<_> = params.iter()
|
// parameters that are present have no bounds.
|
||||||
.filter_map(|param| match *param {
|
let non_lifetime_param_spans: Vec<_> = params.iter()
|
||||||
GenericParamAST::Lifetime(_) => None,
|
.filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Type(ref t) => Some(t.ident.span),
|
GenericParamKindAST::Lifetime { ref bounds, .. } => {
|
||||||
|
if !bounds.is_empty() {
|
||||||
|
let spans: Vec<_> = bounds.iter().map(|b| b.ident.span).collect();
|
||||||
|
self.err_handler().span_err(spans,
|
||||||
|
"lifetime bounds cannot be used in this context");
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => Some(param.ident.span),
|
||||||
}).collect();
|
}).collect();
|
||||||
if !non_lifetime_param_spans.is_empty() {
|
if !non_lifetime_param_spans.is_empty() {
|
||||||
self.err_handler().span_err(non_lifetime_param_spans,
|
self.err_handler().span_err(non_lifetime_param_spans,
|
||||||
"only lifetime parameters can be used in this context");
|
"only lifetime parameters can be used in this context");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check: No bounds on lifetime parameters
|
|
||||||
for param in params.iter() {
|
|
||||||
match *param {
|
|
||||||
GenericParamAST::Lifetime(ref l) => {
|
|
||||||
if !l.bounds.is_empty() {
|
|
||||||
let spans: Vec<_> = l.bounds.iter().map(|b| b.ident.span).collect();
|
|
||||||
self.err_handler().span_err(spans,
|
|
||||||
"lifetime bounds cannot be used in this context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GenericParamAST::Type(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,22 +329,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
}
|
}
|
||||||
ItemKind::TraitAlias(Generics { ref params, .. }, ..) => {
|
ItemKind::TraitAlias(Generics { ref params, .. }, ..) => {
|
||||||
for param in params {
|
for param in params {
|
||||||
if let GenericParamAST::Type(TyParam {
|
match param.kind {
|
||||||
ident,
|
GenericParamKindAST::Lifetime { .. } => {}
|
||||||
ref bounds,
|
GenericParamKindAST::Type { ref bounds, ref default, .. } => {
|
||||||
ref default,
|
if !bounds.is_empty() {
|
||||||
..
|
self.err_handler().span_err(param.ident.span,
|
||||||
}) = *param
|
"type parameters on the left side \
|
||||||
{
|
of a trait alias cannot be \
|
||||||
if !bounds.is_empty() {
|
bounded");
|
||||||
self.err_handler().span_err(ident.span,
|
}
|
||||||
"type parameters on the left side of a \
|
if !default.is_none() {
|
||||||
trait alias cannot be bounded");
|
self.err_handler().span_err(param.ident.span,
|
||||||
}
|
"type parameters on the left side \
|
||||||
if !default.is_none() {
|
of a trait alias cannot have \
|
||||||
self.err_handler().span_err(ident.span,
|
defaults");
|
||||||
"type parameters on the left side of a \
|
}
|
||||||
trait alias cannot have defaults");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,24 +406,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
let mut seen_non_lifetime_param = false;
|
let mut seen_non_lifetime_param = false;
|
||||||
let mut seen_default = None;
|
let mut seen_default = None;
|
||||||
for param in &g.params {
|
for param in &g.params {
|
||||||
match (param, seen_non_lifetime_param) {
|
match (¶m.kind, seen_non_lifetime_param) {
|
||||||
(&GenericParamAST::Lifetime(ref ld), true) => {
|
(GenericParamKindAST::Lifetime { .. }, true) => {
|
||||||
self.err_handler()
|
self.err_handler()
|
||||||
.span_err(ld.lifetime.ident.span, "lifetime parameters must be leading");
|
.span_err(param.ident.span, "lifetime parameters must be leading");
|
||||||
},
|
},
|
||||||
(&GenericParamAST::Lifetime(_), false) => {}
|
(GenericParamKindAST::Lifetime { .. }, false) => {}
|
||||||
_ => {
|
(GenericParamKindAST::Type { ref default, .. }, _) => {
|
||||||
seen_non_lifetime_param = true;
|
seen_non_lifetime_param = true;
|
||||||
|
if default.is_some() {
|
||||||
|
seen_default = Some(param.ident.span);
|
||||||
|
} else if let Some(span) = seen_default {
|
||||||
|
self.err_handler()
|
||||||
|
.span_err(span, "type parameters with a default must be trailing");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let GenericParamAST::Type(ref ty_param @ TyParam { default: Some(_), .. }) = *param {
|
|
||||||
seen_default = Some(ty_param.ident.span);
|
|
||||||
} else if let Some(span) = seen_default {
|
|
||||||
self.err_handler()
|
|
||||||
.span_err(span, "type parameters with a default must be trailing");
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for predicate in &g.where_clause.predicates {
|
for predicate in &g.where_clause.predicates {
|
||||||
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
|
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
|
||||||
|
|
|
@ -56,7 +56,7 @@ use syntax::util::lev_distance::find_best_match_for_name;
|
||||||
use syntax::visit::{self, FnKind, Visitor};
|
use syntax::visit::{self, FnKind, Visitor};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::ast::{Arm, BindingMode, Block, Crate, Expr, ExprKind};
|
use syntax::ast::{Arm, BindingMode, Block, Crate, Expr, ExprKind};
|
||||||
use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamAST, Generics};
|
use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKindAST, Generics};
|
||||||
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
|
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
|
||||||
use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
|
use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
|
||||||
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
|
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
|
||||||
|
@ -797,31 +797,43 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
|
||||||
// put all the parameters on the ban list and then remove
|
// put all the parameters on the ban list and then remove
|
||||||
// them one by one as they are processed and become available.
|
// them one by one as they are processed and become available.
|
||||||
let mut default_ban_rib = Rib::new(ForwardTyParamBanRibKind);
|
let mut default_ban_rib = Rib::new(ForwardTyParamBanRibKind);
|
||||||
|
let mut found_default = false;
|
||||||
default_ban_rib.bindings.extend(generics.params.iter()
|
default_ban_rib.bindings.extend(generics.params.iter()
|
||||||
.filter_map(|p| if let GenericParamAST::Type(ref tp) = *p { Some(tp) } else { None })
|
.filter_map(|param| match param.kind {
|
||||||
.skip_while(|p| p.default.is_none())
|
GenericParamKindAST::Lifetime { .. } => None,
|
||||||
.map(|p| (Ident::with_empty_ctxt(p.ident.name), Def::Err)));
|
GenericParamKindAST::Type { ref default, .. } => {
|
||||||
|
if default.is_some() {
|
||||||
|
found_default = true;
|
||||||
|
}
|
||||||
|
if found_default {
|
||||||
|
return Some((Ident::with_empty_ctxt(param.ident.name), Def::Err));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
for param in &generics.params {
|
for param in &generics.params {
|
||||||
match *param {
|
match param.kind {
|
||||||
GenericParamAST::Lifetime(_) => self.visit_generic_param(param),
|
GenericParamKindAST::Lifetime { .. } => self.visit_generic_param(param),
|
||||||
GenericParamAST::Type(ref ty_param) => {
|
GenericParamKindAST::Type { ref bounds, ref default, .. } => {
|
||||||
for bound in &ty_param.bounds {
|
for bound in bounds {
|
||||||
self.visit_ty_param_bound(bound);
|
self.visit_ty_param_bound(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref ty) = ty_param.default {
|
if let Some(ref ty) = default {
|
||||||
self.ribs[TypeNS].push(default_ban_rib);
|
self.ribs[TypeNS].push(default_ban_rib);
|
||||||
self.visit_ty(ty);
|
self.visit_ty(ty);
|
||||||
default_ban_rib = self.ribs[TypeNS].pop().unwrap();
|
default_ban_rib = self.ribs[TypeNS].pop().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow all following defaults to refer to this type parameter.
|
// Allow all following defaults to refer to this type parameter.
|
||||||
default_ban_rib.bindings.remove(&Ident::with_empty_ctxt(ty_param.ident.name));
|
default_ban_rib.bindings.remove(&Ident::with_empty_ctxt(param.ident.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for p in &generics.where_clause.predicates { self.visit_where_predicate(p); }
|
for p in &generics.where_clause.predicates {
|
||||||
|
self.visit_where_predicate(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2198,25 +2210,28 @@ impl<'a> Resolver<'a> {
|
||||||
let mut function_type_rib = Rib::new(rib_kind);
|
let mut function_type_rib = Rib::new(rib_kind);
|
||||||
let mut seen_bindings = FxHashMap();
|
let mut seen_bindings = FxHashMap();
|
||||||
for param in &generics.params {
|
for param in &generics.params {
|
||||||
if let GenericParamAST::Type(ref type_parameter) = *param {
|
match param.kind {
|
||||||
let ident = type_parameter.ident.modern();
|
GenericParamKindAST::Type { .. } => {
|
||||||
debug!("with_type_parameter_rib: {}", type_parameter.id);
|
let ident = param.ident.modern();
|
||||||
|
debug!("with_type_parameter_rib: {}", param.id);
|
||||||
|
|
||||||
if seen_bindings.contains_key(&ident) {
|
if seen_bindings.contains_key(&ident) {
|
||||||
let span = seen_bindings.get(&ident).unwrap();
|
let span = seen_bindings.get(&ident).unwrap();
|
||||||
let err = ResolutionError::NameAlreadyUsedInTypeParameterList(
|
let err = ResolutionError::NameAlreadyUsedInTypeParameterList(
|
||||||
ident.name,
|
ident.name,
|
||||||
span,
|
span,
|
||||||
);
|
);
|
||||||
resolve_error(self, type_parameter.ident.span, err);
|
resolve_error(self, param.ident.span, err);
|
||||||
|
}
|
||||||
|
seen_bindings.entry(ident).or_insert(param.ident.span);
|
||||||
|
|
||||||
|
// plain insert (no renaming)
|
||||||
|
let def_id = self.definitions.local_def_id(param.id);
|
||||||
|
let def = Def::TyParam(def_id);
|
||||||
|
function_type_rib.bindings.insert(ident, def);
|
||||||
|
self.record_def(param.id, PathResolution::new(def));
|
||||||
}
|
}
|
||||||
seen_bindings.entry(ident).or_insert(type_parameter.ident.span);
|
_ => {}
|
||||||
|
|
||||||
// plain insert (no renaming)
|
|
||||||
let def_id = self.definitions.local_def_id(type_parameter.id);
|
|
||||||
let def = Def::TyParam(def_id);
|
|
||||||
function_type_rib.bindings.insert(ident, def);
|
|
||||||
self.record_def(type_parameter.id, PathResolution::new(def));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.ribs[TypeNS].push(function_type_rib);
|
self.ribs[TypeNS].push(function_type_rib);
|
||||||
|
|
|
@ -370,35 +370,38 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
) {
|
) {
|
||||||
for param in &generics.params {
|
for param in &generics.params {
|
||||||
if let ast::GenericParamAST::Type(ref ty_param) = *param {
|
match param.kind {
|
||||||
let param_ss = ty_param.ident.span;
|
ast::GenericParamKindAST::Lifetime { .. } => {}
|
||||||
let name = escape(self.span.snippet(param_ss));
|
ast::GenericParamKindAST::Type { .. } => {
|
||||||
// Append $id to name to make sure each one is unique
|
let param_ss = param.ident.span;
|
||||||
let qualname = format!("{}::{}${}", prefix, name, id);
|
let name = escape(self.span.snippet(param_ss));
|
||||||
if !self.span.filter_generated(Some(param_ss), full_span) {
|
// Append $id to name to make sure each one is unique.
|
||||||
let id = ::id_from_node_id(ty_param.id, &self.save_ctxt);
|
let qualname = format!("{}::{}${}", prefix, name, id);
|
||||||
let span = self.span_from_span(param_ss);
|
if !self.span.filter_generated(Some(param_ss), full_span) {
|
||||||
|
let id = ::id_from_node_id(param.id, &self.save_ctxt);
|
||||||
|
let span = self.span_from_span(param_ss);
|
||||||
|
|
||||||
self.dumper.dump_def(
|
self.dumper.dump_def(
|
||||||
&Access {
|
&Access {
|
||||||
public: false,
|
public: false,
|
||||||
reachable: false,
|
reachable: false,
|
||||||
},
|
},
|
||||||
Def {
|
Def {
|
||||||
kind: DefKind::Type,
|
kind: DefKind::Type,
|
||||||
id,
|
id,
|
||||||
span,
|
span,
|
||||||
name,
|
name,
|
||||||
qualname,
|
qualname,
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
parent: None,
|
parent: None,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
decl_id: None,
|
decl_id: None,
|
||||||
docs: String::new(),
|
docs: String::new(),
|
||||||
sig: None,
|
sig: None,
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1479,14 +1482,17 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
|
||||||
|
|
||||||
fn visit_generics(&mut self, generics: &'l ast::Generics) {
|
fn visit_generics(&mut self, generics: &'l ast::Generics) {
|
||||||
for param in &generics.params {
|
for param in &generics.params {
|
||||||
if let ast::GenericParamAST::Type(ref ty_param) = *param {
|
match param.kind {
|
||||||
for bound in ty_param.bounds.iter() {
|
ast::GenericParamKindAST::Lifetime { .. } => {}
|
||||||
if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
|
ast::GenericParamKindAST::Type { ref bounds, ref default, .. } => {
|
||||||
self.process_path(trait_ref.trait_ref.ref_id, &trait_ref.trait_ref.path)
|
for bound in bounds {
|
||||||
|
if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
|
||||||
|
self.process_path(trait_ref.trait_ref.ref_id, &trait_ref.trait_ref.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref ty) = default {
|
||||||
|
self.visit_ty(&ty);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if let Some(ref ty) = ty_param.default {
|
|
||||||
self.visit_ty(&ty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -934,9 +934,9 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
|
||||||
sig.push_str(&generics
|
sig.push_str(&generics
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| match *param {
|
.map(|param| match param.kind {
|
||||||
ast::GenericParamAST::Lifetime(ref l) => l.lifetime.ident.name.to_string(),
|
ast::GenericParamKindAST::Lifetime { .. } => param.ident.name.to_string(),
|
||||||
ast::GenericParamAST::Type(ref t) => t.ident.to_string(),
|
ast::GenericParamKindAST::Type { .. } => param.ident.to_string(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "));
|
.join(", "));
|
||||||
|
|
|
@ -223,9 +223,9 @@ impl Sig for ast::Ty {
|
||||||
text.push_str("for<");
|
text.push_str("for<");
|
||||||
text.push_str(&f.generic_params
|
text.push_str(&f.generic_params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|p| match *p {
|
.filter_map(|param| match param.kind {
|
||||||
ast::GenericParamAST::Lifetime(ref l) => {
|
ast::GenericParamKindAST::Lifetime { .. } => {
|
||||||
Some(l.lifetime.ident.to_string())
|
Some(param.ident.to_string())
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
@ -617,45 +617,34 @@ impl Sig for ast::Generics {
|
||||||
|
|
||||||
let mut defs = vec![];
|
let mut defs = vec![];
|
||||||
for param in &self.params {
|
for param in &self.params {
|
||||||
match *param {
|
let mut param_text = param.ident.to_string();
|
||||||
ast::GenericParamAST::Lifetime(ref l) => {
|
defs.push(SigElement {
|
||||||
let mut l_text = l.lifetime.ident.to_string();
|
id: id_from_node_id(param.id, scx),
|
||||||
defs.push(SigElement {
|
start: offset + text.len(),
|
||||||
id: id_from_node_id(l.lifetime.id, scx),
|
end: offset + text.len() + param_text.len(),
|
||||||
start: offset + text.len(),
|
});
|
||||||
end: offset + text.len() + l_text.len(),
|
match param.kind {
|
||||||
});
|
ast::GenericParamKindAST::Lifetime { ref bounds, .. } => {
|
||||||
|
if !bounds.is_empty() {
|
||||||
if !l.bounds.is_empty() {
|
param_text.push_str(": ");
|
||||||
l_text.push_str(": ");
|
let bounds = bounds.iter()
|
||||||
let bounds = l.bounds
|
|
||||||
.iter()
|
|
||||||
.map(|l| l.ident.to_string())
|
.map(|l| l.ident.to_string())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" + ");
|
.join(" + ");
|
||||||
l_text.push_str(&bounds);
|
param_text.push_str(&bounds);
|
||||||
// FIXME add lifetime bounds refs.
|
// FIXME add lifetime bounds refs.
|
||||||
}
|
}
|
||||||
text.push_str(&l_text);
|
|
||||||
text.push(',');
|
|
||||||
}
|
}
|
||||||
ast::GenericParamAST::Type(ref t) => {
|
ast::GenericParamKindAST::Type { ref bounds, .. } => {
|
||||||
let mut t_text = t.ident.to_string();
|
if !bounds.is_empty() {
|
||||||
defs.push(SigElement {
|
param_text.push_str(": ");
|
||||||
id: id_from_node_id(t.id, scx),
|
param_text.push_str(&pprust::bounds_to_string(bounds));
|
||||||
start: offset + text.len(),
|
|
||||||
end: offset + text.len() + t_text.len(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if !t.bounds.is_empty() {
|
|
||||||
t_text.push_str(": ");
|
|
||||||
t_text.push_str(&pprust::bounds_to_string(&t.bounds));
|
|
||||||
// FIXME descend properly into bounds.
|
// FIXME descend properly into bounds.
|
||||||
}
|
}
|
||||||
text.push_str(&t_text);
|
|
||||||
text.push(',');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
text.push_str(¶m_text);
|
||||||
|
text.push(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
text.push('>');
|
text.push('>');
|
||||||
|
|
|
@ -58,14 +58,6 @@ impl fmt::Debug for Lifetime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A "Path" is essentially Rust's notion of a name.
|
/// A "Path" is essentially Rust's notion of a name.
|
||||||
///
|
///
|
||||||
/// It's represented as a sequence of identifiers,
|
/// It's represented as a sequence of identifiers,
|
||||||
|
@ -329,31 +321,38 @@ pub enum TraitBoundModifier {
|
||||||
pub type TyParamBounds = Vec<TyParamBound>;
|
pub type TyParamBounds = Vec<TyParamBound>;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||||
pub struct TyParam {
|
pub enum GenericParamKindAST {
|
||||||
pub attrs: ThinVec<Attribute>,
|
/// A lifetime definition, e.g. `'a: 'b+'c+'d`.
|
||||||
pub ident: Ident,
|
Lifetime {
|
||||||
pub id: NodeId,
|
bounds: Vec<Lifetime>,
|
||||||
pub bounds: TyParamBounds,
|
lifetime: Lifetime,
|
||||||
pub default: Option<P<Ty>>,
|
},
|
||||||
|
Type {
|
||||||
|
bounds: TyParamBounds,
|
||||||
|
default: Option<P<Ty>>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||||
pub enum GenericParamAST {
|
pub struct GenericParamAST {
|
||||||
Lifetime(LifetimeDef),
|
pub ident: Ident,
|
||||||
Type(TyParam),
|
pub id: NodeId,
|
||||||
|
pub attrs: ThinVec<Attribute>,
|
||||||
|
|
||||||
|
pub kind: GenericParamKindAST,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenericParamAST {
|
impl GenericParamAST {
|
||||||
pub fn is_lifetime_param(&self) -> bool {
|
pub fn is_lifetime_param(&self) -> bool {
|
||||||
match *self {
|
match self.kind {
|
||||||
GenericParamAST::Lifetime(_) => true,
|
GenericParamKindAST::Lifetime { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_type_param(&self) -> bool {
|
pub fn is_type_param(&self) -> bool {
|
||||||
match *self {
|
match self.kind {
|
||||||
GenericParamAST::Type(_) => true,
|
GenericParamKindAST::Type { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,10 +382,8 @@ impl Generics {
|
||||||
|
|
||||||
pub fn span_for_name(&self, name: &str) -> Option<Span> {
|
pub fn span_for_name(&self, name: &str) -> Option<Span> {
|
||||||
for param in &self.params {
|
for param in &self.params {
|
||||||
if let GenericParamAST::Type(ref t) = *param {
|
if param.ident.name == name {
|
||||||
if t.ident.name == name {
|
return Some(param.ident.span);
|
||||||
return Some(t.ident.span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub trait AstBuilder {
|
||||||
id: ast::Ident,
|
id: ast::Ident,
|
||||||
attrs: Vec<ast::Attribute>,
|
attrs: Vec<ast::Attribute>,
|
||||||
bounds: ast::TyParamBounds,
|
bounds: ast::TyParamBounds,
|
||||||
default: Option<P<ast::Ty>>) -> ast::TyParam;
|
default: Option<P<ast::Ty>>) -> ast::GenericParamAST;
|
||||||
|
|
||||||
fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
|
fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
|
||||||
fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef;
|
fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef;
|
||||||
|
@ -80,7 +80,7 @@ pub trait AstBuilder {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
attrs: Vec<ast::Attribute>,
|
attrs: Vec<ast::Attribute>,
|
||||||
bounds: Vec<ast::Lifetime>)
|
bounds: Vec<ast::Lifetime>)
|
||||||
-> ast::LifetimeDef;
|
-> ast::GenericParamAST;
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt;
|
fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt;
|
||||||
|
@ -437,13 +437,15 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
attrs: Vec<ast::Attribute>,
|
attrs: Vec<ast::Attribute>,
|
||||||
bounds: ast::TyParamBounds,
|
bounds: ast::TyParamBounds,
|
||||||
default: Option<P<ast::Ty>>) -> ast::TyParam {
|
default: Option<P<ast::Ty>>) -> ast::GenericParamAST {
|
||||||
ast::TyParam {
|
ast::GenericParamAST {
|
||||||
ident: ident.with_span_pos(span),
|
ident: ident.with_span_pos(span),
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
attrs: attrs.into(),
|
attrs: attrs.into(),
|
||||||
bounds,
|
kind: ast::GenericParamKindAST::Type {
|
||||||
default,
|
bounds,
|
||||||
|
default,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,11 +477,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
attrs: Vec<ast::Attribute>,
|
attrs: Vec<ast::Attribute>,
|
||||||
bounds: Vec<ast::Lifetime>)
|
bounds: Vec<ast::Lifetime>)
|
||||||
-> ast::LifetimeDef {
|
-> ast::GenericParamAST {
|
||||||
ast::LifetimeDef {
|
let lifetime = self.lifetime(span, ident);
|
||||||
|
ast::GenericParamAST {
|
||||||
|
ident: lifetime.ident,
|
||||||
|
id: lifetime.id,
|
||||||
attrs: attrs.into(),
|
attrs: attrs.into(),
|
||||||
lifetime: self.lifetime(span, ident),
|
kind: ast::GenericParamKindAST::Lifetime {
|
||||||
bounds,
|
lifetime,
|
||||||
|
bounds,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -687,38 +687,23 @@ 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 {attrs, id, ident, bounds, default} = 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: fld.fold_ident(ident),
|
|
||||||
bounds: fld.fold_bounds(bounds),
|
|
||||||
default: default.map(|x| fld.fold_ty(x)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn noop_fold_generic_param<T: Folder>(param: GenericParamAST, fld: &mut T) -> GenericParamAST {
|
pub fn noop_fold_generic_param<T: Folder>(param: GenericParamAST, fld: &mut T) -> GenericParamAST {
|
||||||
match param {
|
match param {
|
||||||
GenericParamAST::Lifetime(l) => {
|
GenericParamAST::Lifetime { bounds, lifetime } => {
|
||||||
let attrs: Vec<_> = l.attrs.into();
|
let attrs: Vec<_> = param.attrs.into();
|
||||||
GenericParamAST::Lifetime(LifetimeDef {
|
GenericParamAST::Lifetime(LifetimeDef {
|
||||||
attrs: attrs.into_iter()
|
attrs: attrs.into_iter()
|
||||||
.flat_map(|x| fld.fold_attribute(x).into_iter())
|
.flat_map(|x| fld.fold_attribute(x).into_iter())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into(),
|
.into(),
|
||||||
lifetime: Lifetime {
|
lifetime: Lifetime {
|
||||||
id: fld.new_id(l.lifetime.id),
|
id: fld.new_id(param.id),
|
||||||
ident: fld.fold_ident(l.lifetime.ident),
|
ident: fld.fold_ident(param.ident),
|
||||||
},
|
},
|
||||||
bounds: l.bounds.move_map(|l| noop_fold_lifetime(l, fld)),
|
bounds: bounds.move_map(|l| noop_fold_lifetime(l, fld)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
GenericParamAST::Type(t) => GenericParamAST::Type(fld.fold_ty_param(t)),
|
GenericParamAST::Type { .. } => GenericParamAST::Type(fld.fold_ty_param(param)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ use ast::EnumDef;
|
||||||
use ast::{Expr, ExprKind, RangeLimits};
|
use ast::{Expr, ExprKind, RangeLimits};
|
||||||
use ast::{Field, FnDecl};
|
use ast::{Field, FnDecl};
|
||||||
use ast::{ForeignItem, ForeignItemKind, FunctionRetTy};
|
use ast::{ForeignItem, ForeignItemKind, FunctionRetTy};
|
||||||
use ast::GenericParamAST;
|
use ast::{GenericParamAST, GenericParamKindAST};
|
||||||
use ast::GenericArgAST;
|
use ast::GenericArgAST;
|
||||||
use ast::{Ident, ImplItem, IsAuto, Item, ItemKind};
|
use ast::{Ident, ImplItem, IsAuto, Item, ItemKind};
|
||||||
use ast::{Label, Lifetime, LifetimeDef, Lit, LitKind};
|
use ast::{Label, Lifetime, Lit, LitKind};
|
||||||
use ast::Local;
|
use ast::Local;
|
||||||
use ast::MacStmtStyle;
|
use ast::MacStmtStyle;
|
||||||
use ast::{Mac, Mac_, MacDelimiter};
|
use ast::{Mac, Mac_, MacDelimiter};
|
||||||
|
@ -36,7 +36,7 @@ use ast::{VariantData, StructField};
|
||||||
use ast::StrStyle;
|
use ast::StrStyle;
|
||||||
use ast::SelfKind;
|
use ast::SelfKind;
|
||||||
use ast::{TraitItem, TraitRef, TraitObjectSyntax};
|
use ast::{TraitItem, TraitRef, TraitObjectSyntax};
|
||||||
use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
|
use ast::{Ty, TyKind, TypeBinding, TyParamBounds};
|
||||||
use ast::{Visibility, VisibilityKind, WhereClause, CrateSugar};
|
use ast::{Visibility, VisibilityKind, WhereClause, CrateSugar};
|
||||||
use ast::{UseTree, UseTreeKind};
|
use ast::{UseTree, UseTreeKind};
|
||||||
use ast::{BinOpKind, UnOp};
|
use ast::{BinOpKind, UnOp};
|
||||||
|
@ -1311,9 +1311,7 @@ impl<'a> Parser<'a> {
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
|
|
||||||
let (name, node, generics) = if self.eat_keyword(keywords::Type) {
|
let (name, node, generics) = if self.eat_keyword(keywords::Type) {
|
||||||
let (generics, TyParam {ident, bounds, default, ..}) =
|
self.parse_trait_item_assoc_ty()?
|
||||||
self.parse_trait_item_assoc_ty(vec![])?;
|
|
||||||
(ident, TraitItemKind::Type(bounds, default), generics)
|
|
||||||
} else if self.is_const_item() {
|
} else if self.is_const_item() {
|
||||||
self.expect_keyword(keywords::Const)?;
|
self.expect_keyword(keywords::Const)?;
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_ident()?;
|
||||||
|
@ -4805,7 +4803,9 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
|
/// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
|
||||||
fn parse_ty_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, TyParam> {
|
fn parse_ty_param(&mut self,
|
||||||
|
preceding_attrs: Vec<Attribute>)
|
||||||
|
-> PResult<'a, GenericParamAST> {
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_ident()?;
|
||||||
|
|
||||||
// Parse optional colon and param bounds.
|
// Parse optional colon and param bounds.
|
||||||
|
@ -4821,19 +4821,21 @@ impl<'a> Parser<'a> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(TyParam {
|
Ok(GenericParamAST {
|
||||||
attrs: preceding_attrs.into(),
|
|
||||||
ident,
|
ident,
|
||||||
|
attrs: preceding_attrs.into(),
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
bounds,
|
kind: GenericParamKindAST::Type {
|
||||||
default,
|
bounds,
|
||||||
|
default,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the following grammar:
|
/// Parses the following grammar:
|
||||||
/// TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty]
|
/// TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty]
|
||||||
fn parse_trait_item_assoc_ty(&mut self, preceding_attrs: Vec<Attribute>)
|
fn parse_trait_item_assoc_ty(&mut self)
|
||||||
-> PResult<'a, (ast::Generics, TyParam)> {
|
-> PResult<'a, (Ident, TraitItemKind, ast::Generics)> {
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_ident()?;
|
||||||
let mut generics = self.parse_generics()?;
|
let mut generics = self.parse_generics()?;
|
||||||
|
|
||||||
|
@ -4852,13 +4854,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
self.expect(&token::Semi)?;
|
self.expect(&token::Semi)?;
|
||||||
|
|
||||||
Ok((generics, TyParam {
|
Ok((ident, TraitItemKind::Type(bounds, default), generics))
|
||||||
attrs: preceding_attrs.into(),
|
|
||||||
ident,
|
|
||||||
id: ast::DUMMY_NODE_ID,
|
|
||||||
bounds,
|
|
||||||
default,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
|
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
|
||||||
|
@ -4876,18 +4872,22 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
params.push(ast::GenericParamAST::Lifetime(LifetimeDef {
|
params.push(ast::GenericParamAST {
|
||||||
|
ident: lifetime.ident,
|
||||||
|
id: lifetime.id,
|
||||||
attrs: attrs.into(),
|
attrs: attrs.into(),
|
||||||
lifetime,
|
kind: ast::GenericParamKindAST::Lifetime {
|
||||||
bounds,
|
lifetime,
|
||||||
}));
|
bounds,
|
||||||
|
}
|
||||||
|
});
|
||||||
if seen_ty_param {
|
if seen_ty_param {
|
||||||
self.span_err(self.prev_span,
|
self.span_err(self.prev_span,
|
||||||
"lifetime parameters must be declared prior to type parameters");
|
"lifetime parameters must be declared prior to type parameters");
|
||||||
}
|
}
|
||||||
} else if self.check_ident() {
|
} else if self.check_ident() {
|
||||||
// Parse type parameter.
|
// Parse type parameter.
|
||||||
params.push(ast::GenericParamAST::Type(self.parse_ty_param(attrs)?));
|
params.push(self.parse_ty_param(attrs)?);
|
||||||
seen_ty_param = true;
|
seen_ty_param = true;
|
||||||
} else {
|
} else {
|
||||||
// Check for trailing attributes and stop parsing.
|
// Check for trailing attributes and stop parsing.
|
||||||
|
|
|
@ -2878,12 +2878,24 @@ impl<'a> State<'a> {
|
||||||
self.s.word("<")?;
|
self.s.word("<")?;
|
||||||
|
|
||||||
self.commasep(Inconsistent, &generic_params, |s, param| {
|
self.commasep(Inconsistent, &generic_params, |s, param| {
|
||||||
match *param {
|
match param.kind {
|
||||||
ast::GenericParamAST::Lifetime(ref lifetime_def) => {
|
ast::GenericParamKindAST::Lifetime { ref bounds, ref lifetime } => {
|
||||||
s.print_outer_attributes_inline(&lifetime_def.attrs)?;
|
s.print_outer_attributes_inline(¶m.attrs)?;
|
||||||
s.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)
|
s.print_lifetime_bounds(lifetime, bounds)
|
||||||
},
|
},
|
||||||
ast::GenericParamAST::Type(ref ty_param) => s.print_ty_param(ty_param),
|
ast::GenericParamKindAST::Type { ref bounds, ref default } => {
|
||||||
|
s.print_outer_attributes_inline(¶m.attrs)?;
|
||||||
|
s.print_ident(param.ident)?;
|
||||||
|
s.print_bounds(":", bounds)?;
|
||||||
|
match default {
|
||||||
|
Some(ref default) => {
|
||||||
|
s.s.space()?;
|
||||||
|
s.word_space("=")?;
|
||||||
|
s.print_type(default)
|
||||||
|
}
|
||||||
|
_ => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -2891,20 +2903,6 @@ impl<'a> State<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_ty_param(&mut self, param: &ast::TyParam) -> io::Result<()> {
|
|
||||||
self.print_outer_attributes_inline(¶m.attrs)?;
|
|
||||||
self.print_ident(param.ident)?;
|
|
||||||
self.print_bounds(":", ¶m.bounds)?;
|
|
||||||
match param.default {
|
|
||||||
Some(ref default) => {
|
|
||||||
self.s.space()?;
|
|
||||||
self.word_space("=")?;
|
|
||||||
self.print_type(default)
|
|
||||||
}
|
|
||||||
_ => Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_where_clause(&mut self, where_clause: &ast::WhereClause)
|
pub fn print_where_clause(&mut self, where_clause: &ast::WhereClause)
|
||||||
-> io::Result<()> {
|
-> io::Result<()> {
|
||||||
if where_clause.predicates.is_empty() {
|
if where_clause.predicates.is_empty() {
|
||||||
|
|
|
@ -491,17 +491,17 @@ pub fn walk_ty_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a TyPar
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParamAST) {
|
pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParamAST) {
|
||||||
match *param {
|
match param.kind {
|
||||||
GenericParamAST::Lifetime(ref l) => {
|
GenericParamKindAST::Lifetime { ref bounds, ref lifetime, .. } => {
|
||||||
visitor.visit_ident(l.lifetime.ident);
|
visitor.visit_ident(param.ident);
|
||||||
walk_list!(visitor, visit_lifetime, &l.bounds);
|
walk_list!(visitor, visit_lifetime, bounds);
|
||||||
walk_list!(visitor, visit_attribute, &*l.attrs);
|
walk_list!(visitor, visit_attribute, param.attrs.iter());
|
||||||
}
|
}
|
||||||
GenericParamAST::Type(ref t) => {
|
GenericParamKindAST::Type { ref bounds, ref default, .. } => {
|
||||||
visitor.visit_ident(t.ident);
|
visitor.visit_ident(t.ident);
|
||||||
walk_list!(visitor, visit_ty_param_bound, &t.bounds);
|
walk_list!(visitor, visit_ty_param_bound, bounds);
|
||||||
walk_list!(visitor, visit_ty, &t.default);
|
walk_list!(visitor, visit_ty, default);
|
||||||
walk_list!(visitor, visit_attribute, &*t.attrs);
|
walk_list!(visitor, visit_attribute, param.attrs.iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,8 +192,8 @@ use std::collections::HashSet;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use syntax::ast::{self, BinOpKind, EnumDef, Expr, GenericParamAST, Generics, Ident, PatKind};
|
use syntax::ast::{self, BinOpKind, EnumDef, Expr, Generics, Ident, PatKind};
|
||||||
use syntax::ast::{VariantData, GenericArgAST};
|
use syntax::ast::{VariantData, GenericParamKindAST, GenericArgAST};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
|
@ -547,9 +547,9 @@ impl<'a> TraitDef<'a> {
|
||||||
|
|
||||||
// Create the generic parameters
|
// Create the generic parameters
|
||||||
params.extend(generics.params.iter().map(|param| {
|
params.extend(generics.params.iter().map(|param| {
|
||||||
match *param {
|
match param.kind {
|
||||||
ref l @ GenericParamAST::Lifetime(_) => l.clone(),
|
GenericParamKindAST::Lifetime { .. } => param.clone(),
|
||||||
GenericParamAST::Type(ref ty_param) => {
|
GenericParamKindAST::Type { bounds: ref ty_bounds, .. } => {
|
||||||
// I don't think this can be moved out of the loop, since
|
// I don't think this can be moved out of the loop, since
|
||||||
// a TyParamBound requires an ast id
|
// a TyParamBound requires an ast id
|
||||||
let mut bounds: Vec<_> =
|
let mut bounds: Vec<_> =
|
||||||
|
@ -564,12 +564,11 @@ impl<'a> TraitDef<'a> {
|
||||||
bounds.push(cx.typarambound(trait_path.clone()));
|
bounds.push(cx.typarambound(trait_path.clone()));
|
||||||
|
|
||||||
// also add in any bounds from the declaration
|
// also add in any bounds from the declaration
|
||||||
for declared_bound in ty_param.bounds.iter() {
|
for declared_bound in ty_bounds {
|
||||||
bounds.push((*declared_bound).clone());
|
bounds.push((*declared_bound).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty_param = cx.typaram(self.span, ty_param.ident, vec![], bounds, None);
|
cx.typaram(self.span, param.ident, vec![], bounds, None)
|
||||||
GenericParamAST::Type(ty_param)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -607,8 +606,8 @@ impl<'a> TraitDef<'a> {
|
||||||
// Extra scope required here so ty_params goes out of scope before params is moved
|
// Extra scope required here so ty_params goes out of scope before params is moved
|
||||||
|
|
||||||
let mut ty_params = params.iter()
|
let mut ty_params = params.iter()
|
||||||
.filter_map(|param| match *param {
|
.filter_map(|param| match param.kind {
|
||||||
ast::GenericParamAST::Type(ref t) => Some(t),
|
ast::GenericParamKindAST::Type { .. } => Some(param),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.peekable();
|
.peekable();
|
||||||
|
@ -668,17 +667,16 @@ impl<'a> TraitDef<'a> {
|
||||||
// Create the type parameters on the `self` path.
|
// Create the type parameters on the `self` path.
|
||||||
let self_ty_params: Vec<P<ast::Ty>> = generics.params
|
let self_ty_params: Vec<P<ast::Ty>> = generics.params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|param| match *param {
|
.filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Type(ref ty_param)
|
GenericParamKindAST::Type { .. } => Some(cx.ty_ident(self.span, param.ident)),
|
||||||
=> Some(cx.ty_ident(self.span, ty_param.ident)),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let self_lifetimes: Vec<ast::Lifetime> = generics.params
|
let self_lifetimes: Vec<ast::Lifetime> = generics.params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|param| match *param {
|
.filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ref ld) => Some(ld.lifetime),
|
GenericParamKindAST::Lifetime { ref lifetime, .. } => Some(*lifetime),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub use self::PtrTy::*;
|
||||||
pub use self::Ty::*;
|
pub use self::Ty::*;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast::{Expr, GenericParamAST, Generics, Ident, SelfKind, GenericArgAST};
|
use syntax::ast::{Expr, GenericParamKindAST, Generics, Ident, SelfKind, GenericArgAST};
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
use syntax::codemap::{respan, DUMMY_SP};
|
use syntax::codemap::{respan, DUMMY_SP};
|
||||||
|
@ -191,9 +191,9 @@ impl<'a> Ty<'a> {
|
||||||
Self_ => {
|
Self_ => {
|
||||||
let ty_params: Vec<P<ast::Ty>> = self_generics.params
|
let ty_params: Vec<P<ast::Ty>> = self_generics.params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|param| match *param {
|
.filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Type(ref ty_param) => {
|
GenericParamKindAST::Type { .. } => {
|
||||||
Some(cx.ty_ident(span, ty_param.ident))
|
Some(cx.ty_ident(span, param.ident))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
@ -201,8 +201,8 @@ impl<'a> Ty<'a> {
|
||||||
|
|
||||||
let lifetimes: Vec<ast::Lifetime> = self_generics.params
|
let lifetimes: Vec<ast::Lifetime> = self_generics.params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|param| match *param {
|
.filter_map(|param| match param.kind {
|
||||||
GenericParamAST::Lifetime(ref ld) => Some(ld.lifetime),
|
GenericParamKindAST::Lifetime { ref lifetime, .. } => Some(*lifetime),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -234,7 +234,7 @@ fn mk_ty_param(cx: &ExtCtxt,
|
||||||
bounds: &[Path],
|
bounds: &[Path],
|
||||||
self_ident: Ident,
|
self_ident: Ident,
|
||||||
self_generics: &Generics)
|
self_generics: &Generics)
|
||||||
-> ast::TyParam {
|
-> ast::GenericParamAST {
|
||||||
let bounds = bounds.iter()
|
let bounds = bounds.iter()
|
||||||
.map(|b| {
|
.map(|b| {
|
||||||
let path = b.to_path(cx, span, self_ident, self_generics);
|
let path = b.to_path(cx, span, self_ident, self_generics);
|
||||||
|
@ -244,7 +244,7 @@ fn mk_ty_param(cx: &ExtCtxt,
|
||||||
cx.typaram(span, cx.ident_of(name), attrs.to_owned(), bounds, None)
|
cx.typaram(span, cx.ident_of(name), attrs.to_owned(), bounds, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_generics(params: Vec<GenericParamAST>, span: Span) -> Generics {
|
fn mk_generics(params: Vec<ast::GenericParamAST>, span: Span) -> Generics {
|
||||||
Generics {
|
Generics {
|
||||||
params,
|
params,
|
||||||
where_clause: ast::WhereClause {
|
where_clause: ast::WhereClause {
|
||||||
|
@ -282,16 +282,13 @@ impl<'a> LifetimeBounds<'a> {
|
||||||
let bounds = bounds.iter()
|
let bounds = bounds.iter()
|
||||||
.map(|b| cx.lifetime(span, Ident::from_str(b)))
|
.map(|b| cx.lifetime(span, Ident::from_str(b)))
|
||||||
.collect();
|
.collect();
|
||||||
let lifetime_def = cx.lifetime_def(span, Ident::from_str(lt), vec![], bounds);
|
cx.lifetime_def(span, Ident::from_str(lt), vec![], bounds)
|
||||||
GenericParamAST::Lifetime(lifetime_def)
|
|
||||||
})
|
})
|
||||||
.chain(self.bounds
|
.chain(self.bounds
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
let (name, ref bounds) = *t;
|
let (name, ref bounds) = *t;
|
||||||
GenericParamAST::Type(mk_ty_param(
|
mk_ty_param(cx, span, name, &[], &bounds, self_ty, self_generics)
|
||||||
cx, span, name, &[], &bounds, self_ty, self_generics
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -134,9 +134,12 @@ fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
|
||||||
match item.node {
|
match item.node {
|
||||||
ast::ItemKind::Struct(_, ast::Generics { ref params, .. }) |
|
ast::ItemKind::Struct(_, ast::Generics { ref params, .. }) |
|
||||||
ast::ItemKind::Enum(_, ast::Generics { ref params, .. }) => {
|
ast::ItemKind::Enum(_, ast::Generics { ref params, .. }) => {
|
||||||
for param in params.iter() {
|
for param in params {
|
||||||
if let ast::GenericParamAST::Type(ref ty) = *param {
|
match param.kind {
|
||||||
typaram.push_str(&ty.ident.as_str());
|
ast::GenericParamKindAST::Type { .. } => {
|
||||||
|
typaram.push_str(¶m.ident.as_str());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue