blob: f88112db0d2d87df2070a0cf1792d056c7ce2cce [file] [log] [blame]
#![doc(html_root_url = "https://dtolnay.github.io/syn")]
#![cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))]
extern crate proc_macro;
extern crate proc_macro2;
extern crate unicode_xid;
#[cfg(any(feature = "printing", feature = "parsing"))]
extern crate quote;
#[macro_use]
extern crate synom;
#[macro_use]
mod macros;
mod attr;
pub use attr::{Attribute, AttrStyle, MetaItem, NestedMetaItem, MetaItemList,
MetaNameValue};
mod data;
pub use data::{Field, Variant, VariantData, Visibility, VisRestricted, VisCrate,
VisPublic, VisInherited};
mod expr;
pub use expr::{Expr, ExprKind, ExprBox, ExprInPlace, ExprArray, ExprCall,
ExprMethodCall, ExprTup, ExprBinary, ExprUnary, ExprCast,
ExprType, ExprIf, ExprIfLet, ExprWhile, ExprWhileLet,
ExprForLoop, ExprLoop, ExprMatch, ExprClosure, ExprBlock,
ExprAssign, ExprAssignOp, ExprField, ExprTupField, ExprIndex,
ExprRange, ExprPath, ExprAddrOf, ExprBreak, ExprContinue,
ExprRet, ExprStruct, ExprRepeat, ExprParen, ExprTry, ExprCatch,
ExprGroup, ExprYield, ExprUnsafe};
#[cfg(feature = "full")]
pub use expr::{Arm, BindingMode, Block, CaptureBy, FieldPat, FieldValue, Local,
MacStmtStyle, Pat, RangeLimits, Stmt, PatIdent, PatWild,
PatStruct, PatTuple, PatTupleStruct, PatPath, PatBox, PatRef,
PatLit, PatRange, PatSlice, InPlaceKind};
mod generics;
pub use generics::{Generics, GenericParam, LifetimeDef, TraitBoundModifier, TypeParam, TypeParamBound,
WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate,
WhereRegionPredicate, BoundLifetimes};
#[cfg(feature = "printing")]
pub use generics::{ImplGenerics, Turbofish, TypeGenerics};
mod ident;
pub use ident::Ident;
#[cfg(feature = "full")]
mod item;
#[cfg(feature = "full")]
pub use item::{Constness, Defaultness, FnArg, FnDecl, ForeignItem, ItemForeignMod,
ImplItem, ImplPolarity, Item, MethodSig, PathListItem,
TraitItem, ViewPath, ItemExternCrate, ItemUse,
ItemStatic, ItemConst, ItemFn, ItemMacro, ItemMod, ItemType, ItemEnum,
ItemStruct, ItemUnion, ItemTrait, ItemDefaultImpl, ItemImpl,
PathSimple, PathGlob, PathList, ForeignItemFn, ForeignItemStatic, ForeignItemType,
TraitItemConst, TraitItemMacro, TraitItemMethod, TraitItemType,
ImplItemConst, ImplItemMacro, ImplItemMethod, ImplItemType, ArgSelfRef,
ArgSelf, ArgCaptured};
#[cfg(feature = "full")]
mod file;
#[cfg(feature = "full")]
pub use file::File;
mod lifetime;
pub use lifetime::Lifetime;
mod lit;
pub use lit::{Lit, LitKind};
mod mac;
pub use mac::{Macro, TokenTree};
mod derive;
pub use derive::{Body, DeriveInput, BodyEnum, BodyStruct};
mod op;
pub use op::{BinOp, UnOp};
mod ty;
pub use ty::{Abi, AbiKind, AngleBracketedParameterData, BareFnArg, BareFnArgName, BareFnType,
ReturnType, MutType, Mutability, ParenthesizedParameterData, Path,
PathParameters, PathSegment, PolyTraitRef, QSelf, Type, TypeBinding, Unsafety,
TypeSlice, TypeArray, TypePtr, TypeReference, TypeBareFn, TypeNever, TypeTup, TypePath,
TypeTraitObject, TypeImplTrait, TypeParen, TypeInfer, TypeGroup};
#[cfg(feature = "printing")]
pub use ty::PathTokens;
pub use synom::span::Span;
pub use synom::tokens;
pub use synom::delimited;
mod gen {
#[cfg(feature = "visit")]
pub mod visit;
#[cfg(feature = "visit_mut")]
pub mod visit_mut;
#[cfg(feature = "fold")]
pub mod fold;
}
pub use gen::*;
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "parsing")]
pub use synom::ParseError;
#[cfg(feature = "parsing")]
use synom::{Synom, SynomBuffer};
/// Parse tokens of source code into the chosen syn data type.
///
/// This is preferred over parsing a string because tokens are able to preserve
/// information about where in the user's code they were originally written (the
/// "span" of the token), possibly allowing the compiler to produce better error
/// messages.
///
/// # Examples
///
/// ```rust,ignore
/// extern crate proc_macro;
/// use proc_macro::TokenStream;
///
/// extern crate syn;
///
/// #[macro_use]
/// extern crate quote;
///
/// use syn::DeriveInput;
///
/// #[proc_macro_derive(MyMacro)]
/// pub fn my_macro(input: TokenStream) -> TokenStream {
/// // Parse the tokens into a syntax tree
/// let ast: DeriveInput = syn::parse(input).unwrap();
///
/// // Build the output, possibly using quasi-quotation
/// let expanded = quote! {
/// /* ... */
/// };
///
/// // Parse back to a token stream and return it
/// expanded.parse().unwrap()
/// }
/// ```
#[cfg(feature = "parsing")]
pub fn parse<T>(tokens: proc_macro::TokenStream) -> Result<T, ParseError>
where T: Synom,
{
_parse(tokens.into())
}
#[cfg(feature = "parsing")]
fn _parse<T>(tokens: proc_macro2::TokenStream) -> Result<T, ParseError>
where T: Synom,
{
let buf = SynomBuffer::new(tokens);
let result = T::parse(buf.begin());
let err = match result {
Ok((rest, t)) => {
if rest.eof() {
return Ok(t);
} else if rest == buf.begin() {
// parsed nothing
ParseError::new("failed to parse anything")
} else {
ParseError::new("failed to parse all tokens")
}
}
Err(err) => err,
};
match T::description() {
Some(s) => Err(ParseError::new(format!("failed to parse {}: {}", s, err))),
None => Err(err),
}
}
/// Parse a `quote::Tokens` of Rust code into the chosen syn data type.
///
/// # Examples
///
/// ```rust
/// extern crate syn;
/// #
/// # #[macro_use]
/// # extern crate quote;
/// #
/// # type Result<T> = std::result::Result<T, Box<std::error::Error>>;
///
/// use syn::Expr;
///
/// fn run() -> Result<()> {
/// let code = quote!(assert_eq!(u8::max_value(), 255));
/// let expr = syn::parse_tokens::<Expr>(code)?;
/// println!("{:#?}", expr);
/// Ok(())
/// }
/// #
/// # fn main() { run().unwrap() }
/// ```
#[cfg(feature = "parsing")]
pub fn parse_tokens<T: Synom>(tokens: quote::Tokens) -> Result<T, ParseError> {
_parse(tokens.into())
}
/// Parse a string of Rust code into the chosen syn data type.
///
/// # Examples
///
/// ```rust
/// extern crate syn;
/// #
/// #
/// # type Result<T> = std::result::Result<T, Box<std::error::Error>>;
///
/// use syn::Expr;
///
/// fn run() -> Result<()> {
/// let code = "assert_eq!(u8::max_value(), 255)";
/// let expr = syn::parse_str::<Expr>(code)?;
/// println!("{:#?}", expr);
/// Ok(())
/// }
/// #
/// # fn main() { run().unwrap() }
/// ```
#[cfg(feature = "parsing")]
pub fn parse_str<T: Synom>(s: &str) -> Result<T, ParseError> {
_parse(s.parse()?)
}
// FIXME the name parse_file makes it sound like you might pass in a path to a
// file, rather than the content.
/// Parse the content of a file of Rust code.
///
/// This is different from `syn::parse_str::<File>(content)` in two ways:
///
/// - It discards a leading byte order mark `\u{FEFF}` if the file has one.
/// - It preserves the shebang line of the file, such as `#!/usr/bin/env rustx`.
///
/// If present, either of these would be an error using `from_str`.
///
/// # Examples
///
/// ```rust,no_run
/// extern crate syn;
/// #
/// #
/// # type Result<T> = std::result::Result<T, Box<std::error::Error>>;
///
/// use std::fs::File;
/// use std::io::Read;
///
/// fn run() -> Result<()> {
/// let mut file = File::open("path/to/code.rs")?;
/// let mut content = String::new();
/// file.read_to_string(&mut content)?;
///
/// let ast = syn::parse_file(&content)?;
/// if let Some(shebang) = ast.shebang {
/// println!("{}", shebang);
/// }
/// println!("{} items", ast.items.len());
///
/// Ok(())
/// }
/// #
/// # fn main() { run().unwrap() }
/// ```
#[cfg(all(feature = "parsing", feature = "full"))]
pub fn parse_file(mut content: &str) -> Result<File, ParseError> {
// Strip the BOM if it is present
const BOM: &'static str = "\u{feff}";
if content.starts_with(BOM) {
content = &content[BOM.len()..];
}
let mut shebang = None;
if content.starts_with("#!") && !content.starts_with("#![") {
if let Some(idx) = content.find('\n') {
shebang = Some(content[..idx].to_string());
content = &content[idx..];
} else {
shebang = Some(content.to_string());
content = "";
}
}
let mut file: File = parse_str(content)?;
file.shebang = shebang;
Ok(file)
}
#[cfg(feature = "printing")]
struct TokensOrDefault<'a, T: 'a>(&'a Option<T>);
#[cfg(feature = "printing")]
impl<'a, T> quote::ToTokens for TokensOrDefault<'a, T>
where T: quote::ToTokens + Default,
{
fn to_tokens(&self, tokens: &mut quote::Tokens) {
match *self.0 {
Some(ref t) => t.to_tokens(tokens),
None => T::default().to_tokens(tokens),
}
}
}