Skip to content

Commit

Permalink
Rewrite the AST to be a bit more user-friendly
Browse files Browse the repository at this point in the history
This commit is a relatively large rewrite of the AST that `syn` exposes. The
main change is to expose enums-of-structs rather than
enums-with-huge-tuple-variants. The best example of this is `ItemKind::Fn` which
changed from:

    enum ItemKind {
        Fn(Box<FnDecl>, Unsafety, Constness, Option<Abi>, Generics, Box<Block>),
        ...
    }

to

    enum ItemKind {
        Fn(ItemFn),
        ...
    }

    struct ItemFn {
        decl: Box<FnDecl>,
        unsafety: Unsafety,
        constness: Constness,
        abi: Option<Abi>,
        generics: Generics,
        block: Box<Block>,
    }

This change serves a few purposes:

* It's now much easier to add fields to each variant of the ast, ast struct
  fields tend to be "by default ignored" in most contexts.
* It's much easier to document what each field is, as each field can have
  dedicated documentation.
* There's now canonicalized names for each field (the name of the field) which
  can help match `match` statements more consistent across a codebase.

A downside of this representation is that it can be a little more verbose to
work with in `match` statements and during constructions. Overall though I'd
feel at least that the readability improved significantly despite the extra
words required to do various operations.

Closes dtolnay#136
  • Loading branch information
alexcrichton committed May 23, 2017
1 parent 56c5570 commit 62a0a59
Show file tree
Hide file tree
Showing 22 changed files with 3,398 additions and 2,391 deletions.
22 changes: 14 additions & 8 deletions src/aster/ty.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use {Generics, Lifetime, MutTy, Mutability, Path, QSelf, Ty, TyParamBound};
use {TyPath, TySlice, TyNever, TyInfer, TyTup, TyRptr, TyImplTrait};
use aster::ident::ToIdent;
use aster::invoke::{Invoke, Identity};
use aster::lifetime::IntoLifetime;
Expand Down Expand Up @@ -36,11 +37,11 @@ impl<F> TyBuilder<F>
}

pub fn build_path(self, path: Path) -> F::Result {
self.build(Ty::Path(None, path))
self.build(Ty::Path(TyPath { qself: None, path: path }))
}

pub fn build_qpath(self, qself: QSelf, path: Path) -> F::Result {
self.build(Ty::Path(Some(qself), path))
self.build(Ty::Path(TyPath { qself: Some(qself), path: path }))
}

pub fn path(self) -> PathBuilder<TyPathBuilder<F>> {
Expand Down Expand Up @@ -115,7 +116,7 @@ impl<F> TyBuilder<F>
}

pub fn build_slice(self, ty: Ty) -> F::Result {
self.build(Ty::Slice(Box::new(ty)))
self.build(Ty::Slice(TySlice { ty: Box::new(ty) }))
}

pub fn slice(self) -> TyBuilder<TySliceBuilder<F>> {
Expand All @@ -131,11 +132,11 @@ impl<F> TyBuilder<F>
}

pub fn never(self) -> F::Result {
self.build(Ty::Never)
self.build(Ty::Never(TyNever {}))
}

pub fn infer(self) -> F::Result {
self.build(Ty::Infer)
self.build(Ty::Infer(TyInfer {}))
}

pub fn option(self) -> TyBuilder<TyOptionBuilder<F>> {
Expand Down Expand Up @@ -236,7 +237,10 @@ impl<F> TyRefBuilder<F>
ty: ty,
mutability: self.mutability,
};
self.builder.build(Ty::Rptr(self.lifetime, Box::new(ty)))
self.builder.build(Ty::Rptr(TyRptr {
lifetime: self.lifetime,
ty: Box::new(ty),
}))
}

pub fn ty(self) -> TyBuilder<Self> {
Expand Down Expand Up @@ -432,7 +436,9 @@ impl<F> TyImplTraitTyBuilder<F>

pub fn build(self) -> F::Result {
let bounds = self.bounds;
self.builder.build(Ty::ImplTrait(bounds))
self.builder.build(Ty::ImplTrait(TyImplTrait {
bounds: bounds,
}))
}
}

Expand Down Expand Up @@ -473,7 +479,7 @@ impl<F> TyTupleBuilder<F>
}

pub fn build(self) -> F::Result {
self.builder.build(Ty::Tup(self.tys))
self.builder.build(Ty::Tup(TyTup { tys: self.tys }))
}
}

Expand Down
206 changes: 114 additions & 92 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ use super::*;

use std::iter;

/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Attribute {
pub style: AttrStyle,

/// The path of the attribute.
///
/// E.g. `derive` in `#[derive(Copy)]`
/// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
pub path: Path,

/// Any tokens after the path.
///
/// E.g. `( Copy )` in `#[derive(Copy)]`
/// E.g. `x < 5` in `#[crate::precondition x < 5]`
pub tts: Vec<TokenTree>,

pub is_sugared_doc: bool,
ast_struct! {
/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
pub struct Attribute {
pub style: AttrStyle,

/// The path of the attribute.
///
/// E.g. `derive` in `#[derive(Copy)]`
/// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
pub path: Path,

/// Any tokens after the path.
///
/// E.g. `( Copy )` in `#[derive(Copy)]`
/// E.g. `x < 5` in `#[crate::precondition x < 5]`
pub tts: Vec<TokenTree>,

pub is_sugared_doc: bool,
}
}

impl Attribute {
Expand Down Expand Up @@ -49,7 +50,11 @@ impl Attribute {
if tts.len() >= 3 {
if let TokenTree::Token(Token::Eq) = tts[1] {
if let TokenTree::Token(Token::Literal(ref lit)) = tts[2] {
return Some((NestedMetaItem::MetaItem(MetaItem::NameValue(ident.clone(), lit.clone())), &tts[3..]));
let pair = MetaNameValue {
ident: ident.clone(),
lit: lit.clone(),
};
return Some((NestedMetaItem::MetaItem(MetaItem::NameValue(pair)), &tts[3..]));
}
}
}
Expand All @@ -58,7 +63,11 @@ impl Attribute {
if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, tts: ref inner_tts }) = tts[1] {
return match list_of_nested_meta_items_from_tokens(vec![], inner_tts) {
Some(nested_meta_items) => {
Some((NestedMetaItem::MetaItem(MetaItem::List(ident.clone(), nested_meta_items)), &tts[2..]))
let list = MetaItemList {
ident: ident.clone(),
nested: nested_meta_items,
};
Some((NestedMetaItem::MetaItem(MetaItem::List(list)), &tts[2..]))
}

None => None
Expand Down Expand Up @@ -97,15 +106,21 @@ impl Attribute {
}

if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(vec![], tts) {
return Some(MetaItem::List(name.clone(), nested_meta_items));
return Some(MetaItem::List(MetaItemList {
ident: name.clone(),
nested: nested_meta_items,
}));
}
}
}

if self.tts.len() == 2 {
if let TokenTree::Token(Token::Eq) = self.tts[0] {
if let TokenTree::Token(Token::Literal(ref lit)) = self.tts[1] {
return Some(MetaItem::NameValue(name.clone(), lit.clone()));
return Some(MetaItem::NameValue(MetaNameValue {
ident: name.clone(),
lit: lit.clone(),
}));
}
}
}
Expand All @@ -114,37 +129,60 @@ impl Attribute {
}
}

/// Distinguishes between Attributes that decorate items and Attributes that
/// are contained as statements within items. These two cases need to be
/// distinguished for pretty-printing.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum AttrStyle {
/// Attribute of the form `#![...]`.
Outer,

/// Attribute of the form `#[...]`.
Inner,
ast_enum! {
/// Distinguishes between Attributes that decorate items and Attributes that
/// are contained as statements within items. These two cases need to be
/// distinguished for pretty-printing.
#[derive(Copy)]
pub enum AttrStyle {
/// Attribute of the form `#![...]`.
Outer,

/// Attribute of the form `#[...]`.
Inner,
}
}

/// A compile-time attribute item.
///
/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum MetaItem {
/// Word meta item.
///
/// E.g. `test` as in `#[test]`
Word(Ident),

/// List meta item.
ast_enum_of_structs! {
/// A compile-time attribute item.
///
/// E.g. `derive(..)` as in `#[derive(..)]`
List(Ident, Vec<NestedMetaItem>),

/// Name-value meta item.
///
/// E.g. `feature = "foo"` as in `#[feature = "foo"]`
NameValue(Ident, Lit),
/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
pub enum MetaItem {
/// Word meta item.
///
/// E.g. `test` as in `#[test]`
pub Word(Ident),

/// List meta item.
///
/// E.g. `derive(..)` as in `#[derive(..)]`
pub List(MetaItemList {
/// Name of this attribute.
///
/// E.g. `derive` in `#[derive(..)]`
pub ident: Ident,

/// Arguments to this attribute
///
/// E.g. `..` in `#[derive(..)]`
pub nested: Vec<NestedMetaItem>,
}),

/// Name-value meta item.
///
/// E.g. `feature = "foo"` as in `#[feature = "foo"]`
pub NameValue(MetaNameValue {
/// Name of this attribute.
///
/// E.g. `feature` in `#[feature = "foo"]`
pub ident: Ident,

/// Arguments to this attribute
///
/// E.g. `"foo"` in `#[feature = "foo"]`
pub lit: Lit,
}),
}
}

impl MetaItem {
Expand All @@ -154,27 +192,28 @@ impl MetaItem {
/// `feature` as in `#[feature = "foo"]`.
pub fn name(&self) -> &str {
match *self {
MetaItem::Word(ref name) |
MetaItem::List(ref name, _) |
MetaItem::NameValue(ref name, _) => name.as_ref(),
MetaItem::Word(ref name) => name.as_ref(),
MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
MetaItem::List(ref list) => list.ident.as_ref(),
}
}
}

/// Possible values inside of compile-time attribute lists.
///
/// E.g. the '..' in `#[name(..)]`.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum NestedMetaItem {
/// A full `MetaItem`.
ast_enum_of_structs! {
/// Possible values inside of compile-time attribute lists.
///
/// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
MetaItem(MetaItem),

/// A Rust literal.
///
/// E.g. `"name"` in `#[rename("name")]`.
Literal(Lit),
/// E.g. the '..' in `#[name(..)]`.
pub enum NestedMetaItem {
/// A full `MetaItem`.
///
/// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
pub MetaItem(MetaItem),

/// A Rust literal.
///
/// E.g. `"name"` in `#[rename("name")]`.
pub Literal(Lit),
}
}

pub trait FilterAttrs<'a> {
Expand Down Expand Up @@ -378,37 +417,20 @@ mod printing {
}
}

impl ToTokens for MetaItem {
impl ToTokens for MetaItemList {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
MetaItem::Word(ref ident) => {
ident.to_tokens(tokens);
}
MetaItem::List(ref ident, ref inner) => {
ident.to_tokens(tokens);
tokens.append("(");
tokens.append_separated(inner, ",");
tokens.append(")");
}
MetaItem::NameValue(ref name, ref value) => {
name.to_tokens(tokens);
tokens.append("=");
value.to_tokens(tokens);
}
}
self.ident.to_tokens(tokens);
tokens.append("(");
tokens.append_separated(&self.nested, ",");
tokens.append(")");
}
}

impl ToTokens for NestedMetaItem {
impl ToTokens for MetaNameValue {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
NestedMetaItem::MetaItem(ref nested) => {
nested.to_tokens(tokens);
}
NestedMetaItem::Literal(ref lit) => {
lit.to_tokens(tokens);
}
}
self.ident.to_tokens(tokens);
tokens.append("=");
self.lit.to_tokens(tokens);
}
}
}
Loading

0 comments on commit 62a0a59

Please sign in to comment.