Skip to content

Commit

Permalink
Provide an exhaustive matching idiom for syntax tree enums
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Jan 24, 2021
1 parent d6bbbe2 commit 8b70266
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 14 deletions.
27 changes: 23 additions & 4 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,27 @@ ast_enum_of_structs! {
/// A yield expression: `yield expr`.
Yield(ExprYield),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// Expr::Array(e) => {...}
// Expr::Assign(e) => {...}
// ...
// Expr::Yield(e) => {...}
//
// #[cfg(test)]
// Expr::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down Expand Up @@ -804,7 +823,7 @@ impl Expr {
| Expr::TryBlock(ExprTryBlock { attrs, .. })
| Expr::Yield(ExprYield { attrs, .. }) => mem::replace(attrs, new),
Expr::Verbatim(_) => Vec::new(),
Expr::__Nonexhaustive => unreachable!(),
Expr::TestExhaustive => unreachable!(),
}
}
}
Expand Down Expand Up @@ -2303,7 +2322,7 @@ pub(crate) mod parsing {
Pat::Type(_) => unreachable!(),
Pat::Verbatim(_) => {}
Pat::Wild(pat) => pat.attrs = attrs,
Pat::__Nonexhaustive => unreachable!(),
Pat::TestExhaustive => unreachable!(),
}
Ok(pat)
}
Expand Down Expand Up @@ -2654,7 +2673,7 @@ pub(crate) mod parsing {
}
for part in float_repr.split('.') {
let index = crate::parse_str(part).map_err(|err| Error::new(float.span(), err))?;
let base = mem::replace(e, Expr::__Nonexhaustive);
let base = mem::replace(e, Expr::TestExhaustive);
*e = Expr::Field(ExprField {
attrs: Vec::new(),
base: Box::new(base),
Expand Down
92 changes: 84 additions & 8 deletions src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,27 @@ ast_enum_of_structs! {
/// Tokens forming an item not interpreted by Syn.
Verbatim(TokenStream),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// Item::Const(e) => {...}
// Item::Enum(e) => {...}
// ...
// Item::Verbatim(e) => {...}
//
// #[cfg(test)]
// Item::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down Expand Up @@ -357,7 +376,7 @@ impl Item {
| Item::Macro(ItemMacro { attrs, .. })
| Item::Macro2(ItemMacro2 { attrs, .. }) => mem::replace(attrs, new),
Item::Verbatim(_) => Vec::new(),
Item::__Nonexhaustive => unreachable!(),
Item::TestExhaustive => unreachable!(),
}
}
}
Expand Down Expand Up @@ -553,8 +572,27 @@ ast_enum_of_structs! {
/// Tokens in an `extern` block not interpreted by Syn.
Verbatim(TokenStream),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// ForeignItem::Fn(e) => {...}
// ForeignItem::Static(e) => {...}
// ...
// ForeignItem::Verbatim(e) => {...}
//
// #[cfg(test)]
// ForeignItem::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down Expand Up @@ -641,8 +679,27 @@ ast_enum_of_structs! {
/// Tokens within the definition of a trait not interpreted by Syn.
Verbatim(TokenStream),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// TraitItem::Const(e) => {...}
// TraitItem::Method(e) => {...}
// ...
// TraitItem::Verbatim(e) => {...}
//
// #[cfg(test)]
// TraitItem::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down Expand Up @@ -731,8 +788,27 @@ ast_enum_of_structs! {
/// Tokens within an impl block not interpreted by Syn.
Verbatim(TokenStream),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// ImplItem::Const(e) => {...}
// ImplItem::Method(e) => {...}
// ...
// ImplItem::Verbatim(e) => {...}
//
// #[cfg(test)]
// ImplItem::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down Expand Up @@ -1695,7 +1771,7 @@ pub mod parsing {
ForeignItem::Type(item) => &mut item.attrs,
ForeignItem::Macro(item) => &mut item.attrs,
ForeignItem::Verbatim(_) => return Ok(item),
ForeignItem::__Nonexhaustive => unreachable!(),
ForeignItem::TestExhaustive => unreachable!(),
};
attrs.extend(item_attrs.drain(..));
*item_attrs = attrs;
Expand Down Expand Up @@ -2173,7 +2249,7 @@ pub mod parsing {
TraitItem::Method(item) => &mut item.attrs,
TraitItem::Type(item) => &mut item.attrs,
TraitItem::Macro(item) => &mut item.attrs,
TraitItem::Verbatim(_) | TraitItem::__Nonexhaustive => unreachable!(),
TraitItem::Verbatim(_) | TraitItem::TestExhaustive => unreachable!(),
};
attrs.extend(item_attrs.drain(..));
*item_attrs = attrs;
Expand Down Expand Up @@ -2504,7 +2580,7 @@ pub mod parsing {
ImplItem::Type(item) => &mut item.attrs,
ImplItem::Macro(item) => &mut item.attrs,
ImplItem::Verbatim(_) => return Ok(item),
ImplItem::__Nonexhaustive => unreachable!(),
ImplItem::TestExhaustive => unreachable!(),
};
attrs.extend(item_attrs.drain(..));
*item_attrs = attrs;
Expand Down
21 changes: 20 additions & 1 deletion src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,27 @@ ast_enum_of_structs! {
/// A pattern that matches any value: `_`.
Wild(PatWild),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// Pat::Box(e) => {...}
// Pat::Ident(e) => {...}
// ...
// Pat::Wild(e) => {...}
//
// #[cfg(test)]
// Pat::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down
21 changes: 20 additions & 1 deletion src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,27 @@ ast_enum_of_structs! {
/// Tokens in type position not interpreted by Syn.
Verbatim(TokenStream),

// The following is the only supported idiom for exhaustive matching of
// this enum.
//
// match expr {
// Type::Array(e) => {...}
// Type::BareFn(e) => {...}
// ...
// Type::Verbatim(e) => {...}
//
// #[cfg(test)]
// Type::TestExhaustive => unimplemented!(),
// #[cfg(not(test))]
// _ => { /* some sane fallback */ }
// }
//
// This way we fail your tests but don't break your library when adding
// a variant. You will be notified by a test failure when a variant is
// added, so that you can add code to handle it, but your library will
// continue to compile and work for downstream users in the interim.
#[doc(hidden)]
__Nonexhaustive,
TestExhaustive,
}
}

Expand Down

0 comments on commit 8b70266

Please sign in to comment.