2019-02-04 21:49:54 +09:00
|
|
|
use crate::deriving::generic::ty::*;
|
2019-12-22 17:42:04 -05:00
|
|
|
use crate::deriving::generic::*;
|
2015-12-10 23:23:14 +09:00
|
|
|
|
2020-02-29 20:37:32 +03:00
|
|
|
use rustc_ast::ptr::P;
|
2021-06-29 20:22:52 -04:00
|
|
|
use rustc_ast::EnumDef;
|
|
|
|
|
use rustc_ast::VariantData;
|
2020-04-27 23:26:11 +05:30
|
|
|
use rustc_ast::{Expr, MetaItem};
|
2021-06-29 20:22:52 -04:00
|
|
|
use rustc_errors::Applicability;
|
2019-12-29 17:23:55 +03:00
|
|
|
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
|
2021-06-29 20:22:52 -04:00
|
|
|
use rustc_span::symbol::Ident;
|
2020-01-01 19:30:57 +01:00
|
|
|
use rustc_span::symbol::{kw, sym};
|
2019-12-31 20:15:40 +03:00
|
|
|
use rustc_span::Span;
|
2021-06-29 20:22:52 -04:00
|
|
|
use smallvec::SmallVec;
|
2014-05-16 00:16:13 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
pub fn expand_deriving_default(
|
|
|
|
|
cx: &mut ExtCtxt<'_>,
|
|
|
|
|
span: Span,
|
|
|
|
|
mitem: &MetaItem,
|
|
|
|
|
item: &Annotatable,
|
|
|
|
|
push: &mut dyn FnMut(Annotatable),
|
|
|
|
|
) {
|
2019-05-22 12:42:23 +10:00
|
|
|
let inline = cx.meta_word(span, sym::inline);
|
2019-07-30 14:12:52 -04:00
|
|
|
let attrs = vec![cx.attribute(inline)];
|
2013-09-11 21:51:13 -07:00
|
|
|
let trait_def = TraitDef {
|
2017-08-06 22:54:09 -07:00
|
|
|
span,
|
2014-02-28 13:09:09 -08:00
|
|
|
attributes: Vec::new(),
|
2020-07-14 15:05:26 +10:00
|
|
|
path: Path::new(vec![kw::Default, sym::Default]),
|
2014-02-28 13:09:09 -08:00
|
|
|
additional_bounds: Vec::new(),
|
2020-07-14 16:19:44 +10:00
|
|
|
generics: Bounds::empty(),
|
2015-08-29 14:50:05 -04:00
|
|
|
is_unsafe: false,
|
2016-08-29 11:14:25 +00:00
|
|
|
supports_unions: false,
|
2016-07-19 23:02:06 +05:30
|
|
|
methods: vec![MethodDef {
|
2020-07-08 11:04:10 +10:00
|
|
|
name: kw::Default,
|
2020-07-14 16:19:44 +10:00
|
|
|
generics: Bounds::empty(),
|
2019-12-22 17:42:04 -05:00
|
|
|
explicit_self: None,
|
|
|
|
|
args: Vec::new(),
|
|
|
|
|
ret_ty: Self_,
|
|
|
|
|
attributes: attrs,
|
|
|
|
|
is_unsafe: false,
|
|
|
|
|
unify_fieldless_variants: false,
|
2021-06-29 20:22:52 -04:00
|
|
|
combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
|
|
|
|
|
match substr.fields {
|
|
|
|
|
StaticStruct(_, fields) => {
|
|
|
|
|
default_struct_substructure(cx, trait_span, substr, fields)
|
|
|
|
|
}
|
|
|
|
|
StaticEnum(enum_def, _) => {
|
|
|
|
|
if !cx.sess.features_untracked().derive_default_enum {
|
|
|
|
|
rustc_session::parse::feature_err(
|
|
|
|
|
cx.parse_sess(),
|
|
|
|
|
sym::derive_default_enum,
|
|
|
|
|
span,
|
|
|
|
|
"deriving `Default` on enums is experimental",
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
|
|
|
|
}
|
|
|
|
|
default_enum_substructure(cx, trait_span, enum_def)
|
|
|
|
|
}
|
|
|
|
|
_ => cx.span_bug(trait_span, "method in `derive(Default)`"),
|
|
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
})),
|
|
|
|
|
}],
|
2015-01-25 00:29:24 -05:00
|
|
|
associated_types: Vec::new(),
|
2013-09-11 21:51:13 -07:00
|
|
|
};
|
2015-05-22 21:10:14 +05:30
|
|
|
trait_def.expand(cx, mitem, item, push)
|
2013-09-11 21:51:13 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-29 20:22:52 -04:00
|
|
|
fn default_struct_substructure(
|
2019-12-22 17:42:04 -05:00
|
|
|
cx: &mut ExtCtxt<'_>,
|
|
|
|
|
trait_span: Span,
|
|
|
|
|
substr: &Substructure<'_>,
|
2021-06-29 20:22:52 -04:00
|
|
|
summary: &StaticFields,
|
2019-12-22 17:42:04 -05:00
|
|
|
) -> P<Expr> {
|
2019-05-22 14:41:15 +10:00
|
|
|
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
|
|
|
|
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
2015-02-01 12:44:15 -05:00
|
|
|
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
|
2013-09-11 21:51:13 -07:00
|
|
|
|
2021-06-29 20:22:52 -04:00
|
|
|
match summary {
|
|
|
|
|
Unnamed(ref fields, is_tuple) => {
|
|
|
|
|
if !is_tuple {
|
|
|
|
|
cx.expr_ident(trait_span, substr.type_ident)
|
|
|
|
|
} else {
|
|
|
|
|
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
|
|
|
|
|
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2021-06-29 20:22:52 -04:00
|
|
|
}
|
|
|
|
|
Named(ref fields) => {
|
|
|
|
|
let default_fields = fields
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
|
|
|
|
|
.collect();
|
|
|
|
|
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_enum_substructure(
|
|
|
|
|
cx: &mut ExtCtxt<'_>,
|
|
|
|
|
trait_span: Span,
|
|
|
|
|
enum_def: &EnumDef,
|
|
|
|
|
) -> P<Expr> {
|
|
|
|
|
let default_variant = match extract_default_variant(cx, enum_def, trait_span) {
|
|
|
|
|
Ok(value) => value,
|
|
|
|
|
Err(()) => return DummyResult::raw_expr(trait_span, true),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// At this point, we know that there is exactly one variant with a `#[default]` attribute. The
|
|
|
|
|
// attribute hasn't yet been validated.
|
|
|
|
|
|
|
|
|
|
if let Err(()) = validate_default_attribute(cx, default_variant) {
|
|
|
|
|
return DummyResult::raw_expr(trait_span, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
|
|
|
|
|
|
|
|
|
cx.expr_path(cx.path(
|
|
|
|
|
default_variant.span,
|
|
|
|
|
vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn extract_default_variant<'a>(
|
|
|
|
|
cx: &mut ExtCtxt<'_>,
|
|
|
|
|
enum_def: &'a EnumDef,
|
|
|
|
|
trait_span: Span,
|
|
|
|
|
) -> Result<&'a rustc_ast::Variant, ()> {
|
|
|
|
|
let default_variants: SmallVec<[_; 1]> = enum_def
|
|
|
|
|
.variants
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let variant = match default_variants.as_slice() {
|
|
|
|
|
[variant] => variant,
|
|
|
|
|
[] => {
|
|
|
|
|
cx.struct_span_err(trait_span, "no default declared")
|
|
|
|
|
.help("make a unit variant default by placing `#[default]` above it")
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
[first, rest @ ..] => {
|
|
|
|
|
cx.struct_span_err(trait_span, "multiple declared defaults")
|
|
|
|
|
.span_label(first.span, "first default")
|
|
|
|
|
.span_labels(rest.iter().map(|variant| variant.span), "additional default")
|
|
|
|
|
.note("only one variant can be default")
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if !matches!(variant.data, VariantData::Unit(..)) {
|
|
|
|
|
cx.struct_span_err(variant.ident.span, "`#[default]` may only be used on unit variants")
|
|
|
|
|
.help("consider a manual implementation of `Default`")
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
|
|
|
|
|
cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
|
|
|
|
|
.span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
|
|
|
|
|
.help("consider a manual implementation of `Default`")
|
2019-12-31 21:25:16 +01:00
|
|
|
.emit();
|
2021-06-29 20:22:52 -04:00
|
|
|
|
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(variant)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn validate_default_attribute(
|
|
|
|
|
cx: &mut ExtCtxt<'_>,
|
|
|
|
|
default_variant: &rustc_ast::Variant,
|
|
|
|
|
) -> Result<(), ()> {
|
|
|
|
|
let attrs: SmallVec<[_; 1]> =
|
|
|
|
|
cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
|
|
|
|
|
|
|
|
|
|
let attr = match attrs.as_slice() {
|
|
|
|
|
[attr] => attr,
|
|
|
|
|
[] => cx.bug(
|
|
|
|
|
"this method must only be called with a variant that has a `#[default]` attribute",
|
|
|
|
|
),
|
|
|
|
|
[first, rest @ ..] => {
|
|
|
|
|
// FIXME(jhpratt) Do we want to perform this check? It doesn't exist
|
|
|
|
|
// for `#[inline]`, `#[non_exhaustive]`, and presumably others.
|
|
|
|
|
|
|
|
|
|
let suggestion_text =
|
|
|
|
|
if rest.len() == 1 { "try removing this" } else { "try removing these" };
|
|
|
|
|
|
|
|
|
|
cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
|
|
|
|
|
.note("only one `#[default]` attribute is needed")
|
|
|
|
|
.span_label(first.span, "`#[default]` used here")
|
|
|
|
|
.span_label(rest[0].span, "`#[default]` used again here")
|
|
|
|
|
.span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
|
|
|
|
|
// This would otherwise display the empty replacement, hence the otherwise
|
|
|
|
|
// repetitive `.span_help` call above.
|
|
|
|
|
.tool_only_multipart_suggestion(
|
|
|
|
|
suggestion_text,
|
|
|
|
|
rest.iter().map(|attr| (attr.span, String::new())).collect(),
|
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
return Err(());
|
2013-09-11 21:51:13 -07:00
|
|
|
}
|
2021-06-29 20:22:52 -04:00
|
|
|
};
|
|
|
|
|
if !attr.is_word() {
|
|
|
|
|
cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
|
|
|
|
|
.span_suggestion_hidden(
|
|
|
|
|
attr.span,
|
|
|
|
|
"try using `#[default]`",
|
|
|
|
|
"#[default]".into(),
|
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
return Err(());
|
2020-03-20 15:03:11 +01:00
|
|
|
}
|
2021-06-29 20:22:52 -04:00
|
|
|
Ok(())
|
2013-09-11 21:51:13 -07:00
|
|
|
}
|