diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000000..3d30690f12 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.31.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebf7161687..071aee790d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,14 +74,27 @@ jobs: - run: cargo check --manifest-path examples/lazy-static/example/Cargo.toml - run: cargo check --manifest-path examples/trace-var/example/Cargo.toml + docs: + name: Docs + runs-on: ubuntu-latest + env: + RUSTDOCFLAGS: --cfg=doc_cfg -Dbroken_intra_doc_links + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo doc --all-features + codegen: name: Codegen runs-on: ubuntu-latest + env: + CFG_RELEASE_CHANNEL: dev + CFG_RELEASE: 1.51.0 steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2020-05-15 + toolchain: nightly-2021-01-01 - run: cd codegen && cargo run - run: git diff --exit-code @@ -100,4 +113,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy --all-features + - run: cargo clippy --all-features -- -Dclippy::all -Dclippy::pedantic diff --git a/Cargo.toml b/Cargo.toml index 0e17af4f5a..0737d18f1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "1.0.48" # don't forget to update html_root_url and syn.json +version = "1.0.62" # don't forget to update html_root_url and syn.json authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Parser for Rust source code" @@ -48,7 +48,7 @@ ref-cast = "1.0" regex = "1.0" reqwest = { version = "0.10", features = ["blocking"] } syn-test-suite = { version = "0", path = "tests/features" } -tar = "0.4" +tar = "0.4.16" termcolor = "1.0" walkdir = "2.1" @@ -64,6 +64,7 @@ required-features = ["full", "parsing"] [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "doc_cfg"] [package.metadata.playground] features = ["full", "visit", "visit-mut", "fold", "extra-traits"] diff --git a/build.rs b/build.rs index cf7681c3f9..25190f4c87 100644 --- a/build.rs +++ b/build.rs @@ -15,6 +15,10 @@ fn main() { println!("cargo:rustc-cfg=syn_omit_await_from_token_macro"); } + if compiler.minor < 39 { + println!("cargo:rustc-cfg=syn_no_const_vec_new"); + } + if !compiler.nightly { println!("cargo:rustc-cfg=syn_disable_nightly_tests"); } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 39fa27fa0f..47928f6e0b 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -22,9 +22,9 @@ thiserror = "1.0" toml = "0.5" [dependencies.rustfmt] -package = "rustfmt_lib" -git = "https://github.com/rust-lang-nursery/rustfmt" -rev = "99edc8826ee6d7a5d529e8749cdf4ac999dfd954" +package = "rustfmt-nightly" +git = "https://github.com/rust-lang/rustfmt" +rev = "367a874d04abfb2269ff1ea1974f06640546b7c5" [workspace] # Prefer that `cargo clean` in syn's directory does not require a rebuild of diff --git a/codegen/rust-toolchain b/codegen/rust-toolchain index 8737a7eb56..a2b82fb1f4 100644 --- a/codegen/rust-toolchain +++ b/codegen/rust-toolchain @@ -1 +1 @@ -nightly-2020-05-15 +nightly-2021-01-01 diff --git a/codegen/src/clone.rs b/codegen/src/clone.rs index 979ef0bd47..e5bcea1b81 100644 --- a/codegen/src/clone.rs +++ b/codegen/src/clone.rs @@ -82,8 +82,10 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { if copy { return quote! { #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))] impl Copy for #ident {} #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))] impl Clone for #ident { fn clone(&self) -> Self { *self @@ -96,6 +98,7 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { quote! { #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))] impl Clone for #ident { fn clone(&self) -> Self { #body diff --git a/codegen/src/debug.rs b/codegen/src/debug.rs index d5db1c3ddb..74c8412889 100644 --- a/codegen/src/debug.rs +++ b/codegen/src/debug.rs @@ -81,6 +81,7 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { quote! { #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Debug for #ident { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { #body diff --git a/codegen/src/eq.rs b/codegen/src/eq.rs index 2cfee39b44..a40700e008 100644 --- a/codegen/src/eq.rs +++ b/codegen/src/eq.rs @@ -113,6 +113,7 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { let eq = quote! { #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Eq for #ident {} }; @@ -132,6 +133,7 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { #eq #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl PartialEq for #ident { fn eq(&self, #other: &Self) -> bool { #body diff --git a/codegen/src/file.rs b/codegen/src/file.rs index 5521d75984..0eeb0c74c1 100644 --- a/codegen/src/file.rs +++ b/codegen/src/file.rs @@ -14,14 +14,21 @@ pub fn write>(path: P, content: TokenStream) -> Result<()> { writeln!(formatted)?; let mut config = rustfmt::Config::default(); - config.set().emit_mode(rustfmt::EmitMode::Stdout); - config.set().verbose(rustfmt::Verbosity::Quiet); config.set().format_macro_matchers(true); config.set().normalize_doc_attributes(true); - let mut session = rustfmt::Session::new(config, Some(&mut formatted)); - session.format(rustfmt::Input::Text(content.to_string()))?; - drop(session); + let format_report = rustfmt::format( + rustfmt::Input::Text(content.to_string()), + &config, + rustfmt::OperationSetting { + recursive: false, + verbosity: rustfmt::emitter::Verbosity::Normal, + }, + )?; + + for (_filename, format_result) in format_report.format_result() { + write!(formatted, "{}", format_result.formatted_text())?; + } if path.as_ref().is_file() && fs::read(&path)? == formatted { return Ok(()); diff --git a/codegen/src/hash.rs b/codegen/src/hash.rs index 81e44b41a1..a0d04d95ed 100644 --- a/codegen/src/hash.rs +++ b/codegen/src/hash.rs @@ -131,6 +131,7 @@ fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { quote! { #cfg_features + #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Hash for #ident { fn hash(&self, #state: &mut H) where diff --git a/codegen/src/parse.rs b/codegen/src/parse.rs index 1e0284bcf9..716ba0dc7c 100644 --- a/codegen/src/parse.rs +++ b/codegen/src/parse.rs @@ -17,7 +17,6 @@ const SYN_CRATE_ROOT: &str = "../src/lib.rs"; const TOKEN_SRC: &str = "../src/token.rs"; const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"]; const EXTRA_TYPES: &[&str] = &["Lifetime"]; -const NONEXHAUSTIVE: &str = "__Nonexhaustive"; // NOTE: BTreeMap is used here instead of HashMap to have deterministic output. type ItemLookup = BTreeMap; @@ -63,7 +62,7 @@ fn introspect_item(item: &AstItem, items: &ItemLookup, tokens: &TokenLookup) -> ident: item.ast.ident.to_string(), features, data: types::Data::Enum(introspect_enum(data, items, tokens)), - exhaustive: data.variants.iter().all(|v| v.ident != NONEXHAUSTIVE), + exhaustive: !data.variants.iter().any(|v| is_doc_hidden(&v.attrs)), }, Data::Struct(ref data) => types::Node { ident: item.ast.ident.to_string(), @@ -85,7 +84,7 @@ fn introspect_enum(item: &DataEnum, items: &ItemLookup, tokens: &TokenLookup) -> item.variants .iter() .filter_map(|variant| { - if variant.ident == NONEXHAUSTIVE { + if is_doc_hidden(&variant.attrs) { return None; } let fields = match &variant.fields { @@ -215,6 +214,20 @@ fn is_pub(vis: &Visibility) -> bool { } } +fn is_doc_hidden(attrs: &[Attribute]) -> bool { + for attr in attrs { + if attr.path.is_ident("doc") { + if parsing::parse_doc_hidden_attr + .parse2(attr.tokens.clone()) + .is_ok() + { + return true; + } + } + } + false +} + fn first_arg(params: &PathArguments) -> &syn::Type { let data = match *params { PathArguments::AngleBracketed(ref data) => data, @@ -252,7 +265,7 @@ mod parsing { use proc_macro2::{TokenStream, TokenTree}; use quote::quote; use std::collections::{BTreeMap, BTreeSet}; - use syn::parse::{ParseStream, Result}; + use syn::parse::{ParseStream, Parser, Result}; use syn::{ braced, bracketed, parenthesized, parse_quote, token, Attribute, Ident, LitStr, Path, Token, }; @@ -331,11 +344,12 @@ mod parsing { // A single variant of an ast_enum_of_structs! struct EosVariant { + attrs: Vec, name: Ident, member: Option, } fn eos_variant(input: ParseStream) -> Result { - input.call(Attribute::parse_outer)?; + let attrs = input.call(Attribute::parse_outer)?; let variant: Ident = input.parse()?; let member = if input.peek(token::Paren) { let content; @@ -347,6 +361,7 @@ mod parsing { }; input.parse::()?; Ok(EosVariant { + attrs, name: variant, member, }) @@ -371,10 +386,11 @@ mod parsing { let enum_item = { let variants = variants.iter().map(|v| { - let name = v.name.clone(); + let attrs = &v.attrs; + let name = &v.name; match v.member { - Some(ref member) => quote!(#name(#member)), - None => quote!(#name), + Some(ref member) => quote!(#(#attrs)* #name(#member)), + None => quote!(#(#attrs)* #name), } }); parse_quote! { @@ -390,6 +406,7 @@ mod parsing { } mod kw { + syn::custom_keyword!(hidden); syn::custom_keyword!(macro_rules); syn::custom_keyword!(Token); } @@ -475,6 +492,27 @@ mod parsing { Ok(types::Features { any: features }) } + + pub fn path_attr(attrs: &[Attribute]) -> Result> { + for attr in attrs { + if attr.path.is_ident("path") { + fn parser(input: ParseStream) -> Result { + input.parse::()?; + input.parse() + } + let filename = parser.parse2(attr.tokens.clone())?; + return Ok(Some(filename)); + } + } + Ok(None) + } + + pub fn parse_doc_hidden_attr(input: ParseStream) -> Result<()> { + let content; + parenthesized!(content in input); + content.parse::()?; + Ok(()) + } } fn clone_features(features: &[Attribute]) -> Vec { @@ -565,7 +603,11 @@ fn do_load_file>( // Look up the submodule file, and recursively parse it. // Only handles same-directory .rs file submodules for now. - let path = parent.join(&format!("{}.rs", item.ident)); + let filename = match parsing::path_attr(&item.attrs)? { + Some(filename) => filename.value(), + None => format!("{}.rs", item.ident), + }; + let path = parent.join(filename); load_file(path, &features, lookup)?; } Item::Macro(item) => { diff --git a/dev/main.rs b/dev/main.rs index eb675465f0..8ef674bcd8 100644 --- a/dev/main.rs +++ b/dev/main.rs @@ -1,4 +1,550 @@ syn_dev::r#mod! { // Write Rust code here and run `cargo check` to have Syn parse it. +//! Ergonomic, checked cast functions for primitive types +//! +//! This crate provides one checked cast function for each numeric primitive. +//! Use these functions to perform a cast from any other numeric primitive: +//! +//! ``` +//! extern crate cast; +//! +//! use cast::{u8, u16, Error}; +//! +//! # fn main() { +//! // Infallible operations, like integer promotion, are equivalent to a normal +//! // cast with `as` +//! assert_eq!(u16(0u8), 0u16); +//! +//! // Everything else will return a `Result` depending on the success of the +//! // operation +//! assert_eq!(u8(0u16), Ok(0u8)); +//! assert_eq!(u8(256u16), Err(Error::Overflow)); +//! assert_eq!(u8(-1i8), Err(Error::Underflow)); +//! assert_eq!(u8(1. / 0.), Err(Error::Infinite)); +//! assert_eq!(u8(0. / 0.), Err(Error::NaN)); +//! # } +//! ``` +//! +//! There are no namespace problems between these functions, the "primitive +//! modules" in `core`/`std` and the built-in primitive types, so all them can +//! be in the same scope: +//! +//! ``` +//! extern crate cast; +//! +//! use std::u8; +//! use cast::{u8, u16}; +//! +//! # fn main() { +//! // `u8` as a type +//! let x: u8 = 0; +//! // `u8` as a module +//! let y = u16(u8::MAX); +//! // `u8` as a function +//! let z = u8(y).unwrap(); +//! # } +//! ``` +//! +//! The checked cast functionality is also usable with type aliases via the +//! `cast` static method: +//! +//! ``` +//! extern crate cast; +//! +//! use std::os::raw::c_ulonglong; +//! // NOTE avoid shadowing `std::convert::From` - cf. rust-lang/rfcs#1311 +//! use cast::From as _0; +//! +//! # fn main() { +//! assert_eq!(c_ulonglong::cast(0u8), 0u64); +//! # } +//! ``` +//! +//! This crate also provides a `From` trait that can be used, for example, +//! to create a generic function that accepts any type that can be infallibly +//! casted to `u32`. +//! +//! ``` +//! extern crate cast; +//! +//! fn to_u32(x: T) -> u32 +//! // reads as: "where u32 can be casted from T with output u32" +//! where u32: cast::From, +//! { +//! cast::u32(x) +//! } +//! +//! # fn main() { +//! assert_eq!(to_u32(0u8), 0u32); +//! assert_eq!(to_u32(1u16), 1u32); +//! assert_eq!(to_u32(2u32), 2u32); +//! +//! // to_u32(-1i32); // Compile error +//! # } +//! ``` +//! +//! ## Minimal Supported Rust Version +//! +//! This crate is guaranteed to compile on stable Rust 1.13 and up. It *might* compile on older +//! versions but that may change in any new patch release. +//! +//! ## Building without `std` +//! +//! This crate can be used without Rust's `std` crate by declaring it as +//! follows in your `Cargo.toml`: +//! +//! ``` toml +//! cast = { version = "*", default-features = false } +//! ``` + +#![deny(missing_docs)] +#![deny(warnings)] +#![allow(const_err)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "x128", not(stable_i128)), feature(i128_type, i128))] + +#[cfg(feature = "std")] +extern crate core; + +#[cfg(test)] +#[macro_use] +extern crate quickcheck; + +use core::fmt; +#[cfg(feature = "std")] +use std::error; + +#[cfg(test)] +mod test; + +/// Cast errors +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Error { + /// Infinite value casted to a type that can only represent finite values + Infinite, + /// NaN value casted to a type that can't represent a NaN value + NaN, + /// Source value is greater than the maximum value that the destination type + /// can hold + Overflow, + /// Source value is smaller than the minimum value that the destination type + /// can hold + Underflow, +} + +impl Error { + /// A private helper function that implements `description`, because + /// `description` is only available when we have `std` enabled. + fn description_helper(&self) -> &str { + match *self { + Error::Infinite => "Cannot store infinite value in finite type", + Error::NaN => "Cannot store NaN in type which does not support it", + Error::Overflow => "Overflow during numeric conversion", + Error::Underflow => "Underflow during numeric conversion", + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description_helper()) + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { + fn description(&self) -> &str { + self.description_helper() + } +} + +/// The "cast from" operation +pub trait From { + /// The result of the cast operation: either `Self` or `Result` + type Output; + + /// Checked cast from `Src` to `Self` + fn cast(Src) -> Self::Output; +} + +macro_rules! fns { + ($($ty:ident),+) => { + $( + /// Checked cast function + #[inline] + pub fn $ty(x: T) -> <$ty as From>::Output + where $ty: From + { + <$ty as From>::cast(x) + } + )+ + } +} + +fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + +#[cfg(feature = "x128")] +fns!(i128, u128); + +/// `$dst` can hold any value of `$src` +macro_rules! promotion { + ($($src:ty => $($dst: ty),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = $dst; + + #[inline] + fn cast(src: $src) -> $dst { + src as $dst + } + } + )+ + )+ + } +} + +/// `$dst` can hold any positive value of `$src` +macro_rules! half_promotion { + ($($src:ty => $($dst:ty),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = Result<$dst, Error>; + + #[inline] + fn cast(src: $src) -> Self::Output { + if src < 0 { + Err(Error::Underflow) + } else { + Ok(src as $dst) + } + } + } + )+ + )+ + } +} + +/// From an unsigned `$src` to a smaller `$dst` +macro_rules! from_unsigned { + ($($src:ident => $($dst:ident),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = Result<$dst, Error>; + + #[inline] + fn cast(src: $src) -> Self::Output { + use core::$dst; + + if src > $dst::MAX as $src { + Err(Error::Overflow) + } else { + Ok(src as $dst) + } + } + } + )+ + )+ + } +} + +/// From a signed `$src` to a smaller `$dst` +macro_rules! from_signed { + ($($src:ident => $($dst:ident),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = Result<$dst, Error>; + + #[inline] + fn cast(src: $src) -> Self::Output { + use core::$dst; + + Err(if src < $dst::MIN as $src { + Error::Underflow + } else if src > $dst::MAX as $src { + Error::Overflow + } else { + return Ok(src as $dst); + }) + } + } + )+ + )+ + } +} + +/// From a float `$src` to an integer `$dst` +macro_rules! from_float { + ($($src:ident, $usrc:ident => $($dst:ident),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = Result<$dst, Error>; + + #[inline] + fn cast(src: $src) -> Self::Output { + use core::{$dst, $src}; + + Err(if src != src { + Error::NaN + } else if src == $src::INFINITY || + src == $src::NEG_INFINITY { + Error::Infinite + } else if { + // we subtract 1 ULP (unit of least precision) here because some + // lossy conversions like `u64::MAX as f64` round *up* and we want + // to avoid this evaluating to false in that case + use core::mem::transmute; + let max = unsafe { + transmute::<_, $src>(transmute::<_, $usrc>($dst::MAX as $src) - 1) + }; + src > max + } { + Error::Overflow + } else if $dst::MIN == 0 { + // when casting to unsigned integer, negative values close to 0 but + // larger than 1.0 should be truncated to 0; this behavior matches + // casting from a float to a signed integer + if src <= -1.0 { + Error::Underflow + } else { + return Ok(src as $dst); + } + } else if src < $dst::MIN as $src { + Error::Underflow + } else { + return Ok(src as $dst); + }) + } + } + )+ + )+ + } +} + +/// From a float `$src` to an integer `$dst`, where $dst is large enough to contain +/// all values of `$src`. We can't ever overflow here +#[cfg(feature = "x128")] +macro_rules! from_float_dst { + ($($src:ident => $($dst:ident),+);+;) => { + $( + $( + impl From<$src> for $dst { + type Output = Result<$dst, Error>; + + #[inline] + #[allow(unused_comparisons)] + fn cast(src: $src) -> Self::Output { + use core::{$dst, $src}; + + Err(if src != src { + Error::NaN + } else if src == $src::INFINITY || + src == $src::NEG_INFINITY { + Error::Infinite + } else if ($dst::MIN == 0) && src <= -1.0 { + Error::Underflow + } else { + return Ok(src as $dst); + }) + } + } + )+ + )+ + } +} + +// PLAY TETRIS! ;-) + +#[cfg(target_pointer_width = "32")] +mod _32 { + use {Error, From}; + + // Signed + promotion! { + i8 => f32, f64, i8, i16, i32, isize, i64; + i16 => f32, f64, i16, i32, isize, i64; + i32 => f32, f64, i32, isize, i64; + isize => f32, f64, i32, isize, i64; + i64 => f32, f64, i64; + } + + half_promotion! { + i8 => u8, u16, u32, usize, u64; + i16 => u16, u32, usize, u64; + i32 => u32, usize, u64; + isize => u32, usize, u64; + i64 => u64; + } + + from_signed! { + + i16 => i8, u8; + i32 => i8, i16, u8, u16; + isize => i8, i16, u8, u16; + i64 => i8, i16, i32, isize, u8, u16, u32, usize; + } + + // Unsigned + promotion! { + u8 => f32, f64, i16, i32, isize, i64, u8, u16, u32, usize, u64; + u16 => f32, f64, i32, isize, i64, u16, u32, usize, u64; + u32 => f32, f64, i64, u32, usize, u64; + usize => f32, f64, i64, u32, usize, u64; + u64 => f32, f64, u64; + } + + from_unsigned! { + u8 => i8; + u16 => i8, i16, u8; + u32 => i8, i16, i32, isize, u8, u16; + usize => i8, i16, i32, isize, u8, u16; + u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize; + } + + // Float + promotion! { + f32 => f32, f64; + f64 => f64; + } + + from_float! { + f32, u32 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64; + f64, u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64; + } +} + +#[cfg(target_pointer_width = "64")] +mod _64 { + use {Error, From}; + + // Signed + promotion! { + i8 => f32, f64, i8, i16, i32, i64, isize; + i16 => f32, f64, i16, i32, i64, isize; + i32 => f32, f64, i32, i64, isize; + i64 => f32, f64, i64, isize; + isize => f32, f64, i64, isize; + } + + half_promotion! { + i8 => u8, u16, u32, u64, usize; + i16 => u16, u32, u64, usize; + i32 => u32, u64, usize; + i64 => u64, usize; + isize => u64, usize; + } + + from_signed! { + + i16 => i8, u8; + i32 => i8, i16, u8, u16; + i64 => i8, i16, i32, u8, u16, u32; + isize => i8, i16, i32, u8, u16, u32; + } + + // Unsigned + promotion! { + u8 => f32, f64, i16, i32, i64, isize, u8, u16, u32, u64, usize; + u16 => f32, f64, i32, i64, isize, u16, u32, u64, usize; + u32 => f32, f64, i64, isize, u32, u64, usize; + u64 => f32, f64, u64, usize; + usize => f32, f64, u64, usize; + } + + from_unsigned! { + u8 => i8; + u16 => i8, i16, u8; + u32 => i8, i16, i32, u8, u16; + u64 => i8, i16, i32, i64, isize, u8, u16, u32; + usize => i8, i16, i32, i64, isize, u8, u16, u32; + } + + // Float + promotion! { + f32 => f32, f64; + f64 => f64; + } + + from_float! { + f32, u32 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; + f64, u64 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; + } +} + +#[cfg(feature = "x128")] +mod _x128 { + use {Error, From}; + + // Signed + promotion! { + i8 => i128; + i16 => i128; + i32 => i128; + i64 => i128; + isize => i128; + i128 => f32, f64, i128; + } + + half_promotion! { + i8 => u128; + i16 => u128; + i32 => u128; + i64 => u128; + isize => u128; + i128 => u128; + } + + from_signed! { + i128 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; + } + + // Unsigned + promotion! { + u8 => i128, u128; + u16 => i128, u128; + u32 => i128, u128; + u64 => i128, u128; + usize => i128, u128; + u128 => f64, u128; + } + + from_unsigned! { + u128 => f32, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize; + } + + // Float + from_float! { + f32, u32 => i128; + f64, u64 => i128, u128; + } + + from_float_dst! { + f32 => u128; + } +} + +// The missing piece +impl From for f32 { + type Output = Result; + + #[inline] + fn cast(src: f64) -> Self::Output { + use core::{f32, f64}; + + if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY { + Ok(src as f32) + } else if src < f32::MIN as f64 { + Err(Error::Underflow) + } else if src > f32::MAX as f64 { + Err(Error::Overflow) + } else { + Ok(src as f32) + } + } +} } diff --git a/src/attr.rs b/src/attr.rs index ea6ab09d1b..794a3104f7 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -144,6 +144,7 @@ ast_struct! { /// }; /// assert_eq!(doc, attr); /// ``` + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct Attribute { pub pound_token: Token![#], pub style: AttrStyle, @@ -160,6 +161,7 @@ impl Attribute { /// *This function is available only if Syn is built with the `"parsing"` /// feature.* #[cfg(feature = "parsing")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_meta(&self) -> Result { fn clone_ident_segment(segment: &PathSegment) -> PathSegment { PathSegment { @@ -207,6 +209,7 @@ impl Attribute { /// *This function is available only if Syn is built with the `"parsing"` /// feature.* #[cfg(feature = "parsing")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_args(&self) -> Result { self.parse_args_with(T::parse) } @@ -216,6 +219,7 @@ impl Attribute { /// *This function is available only if Syn is built with the `"parsing"` /// feature.* #[cfg(feature = "parsing")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_args_with(&self, parser: F) -> Result { let parser = |input: ParseStream| { let args = enter_args(self, input)?; @@ -229,6 +233,7 @@ impl Attribute { /// *This function is available only if Syn is built with the `"parsing"` /// feature.* #[cfg(feature = "parsing")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_outer(input: ParseStream) -> Result> { let mut attrs = Vec::new(); while input.peek(Token![#]) { @@ -242,6 +247,7 @@ impl Attribute { /// *This function is available only if Syn is built with the `"parsing"` /// feature.* #[cfg(feature = "parsing")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_inner(input: ParseStream) -> Result> { let mut attrs = Vec::new(); while input.peek(Token![#]) && input.peek2(Token![!]) { @@ -321,6 +327,7 @@ ast_enum! { /// - `#![feature(proc_macro)]` /// - `//! # Example` /// - `/*! Please file an issue */` + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum AttrStyle { Outer, Inner(Token![!]), @@ -350,10 +357,8 @@ ast_enum_of_structs! { /// /// This type is a [syntax tree enum]. /// - /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums - // - // TODO: change syntax-tree-enum link to an intra rustdoc link, currently - // blocked on https://github.com/rust-lang/rust/issues/62833 + /// [syntax tree enum]: Expr#syntax-tree-enums + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum Meta { Path(Path), @@ -370,6 +375,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct MetaList { pub path: Path, pub paren_token: token::Paren, @@ -382,6 +388,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct MetaNameValue { pub path: Path, pub eq_token: Token![=], @@ -408,6 +415,7 @@ ast_enum_of_structs! { /// /// *This type is available only if Syn is built with the `"derive"` or `"full"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum NestedMeta { /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which /// would be a nested `Meta::Path`. @@ -453,6 +461,7 @@ ast_enum_of_structs! { /// # "".parse().unwrap() /// } /// ``` +#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub type AttributeArgs = Vec; pub trait FilterAttrs<'a> { @@ -521,7 +530,7 @@ pub mod parsing { #[cfg(feature = "full")] impl private { - pub fn attrs(outer: Vec, inner: Vec) -> Vec { + pub(crate) fn attrs(outer: Vec, inner: Vec) -> Vec { let mut attrs = outer; attrs.extend(inner); attrs @@ -553,6 +562,7 @@ pub mod parsing { }) } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Meta { fn parse(input: ParseStream) -> Result { let path = input.call(parse_meta_path)?; @@ -560,6 +570,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for MetaList { fn parse(input: ParseStream) -> Result { let path = input.call(parse_meta_path)?; @@ -567,6 +578,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for MetaNameValue { fn parse(input: ParseStream) -> Result { let path = input.call(parse_meta_path)?; @@ -574,6 +586,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for NestedMeta { fn parse(input: ParseStream) -> Result { if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) { @@ -622,6 +635,7 @@ mod printing { use proc_macro2::TokenStream; use quote::ToTokens; + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Attribute { fn to_tokens(&self, tokens: &mut TokenStream) { self.pound_token.to_tokens(tokens); @@ -635,6 +649,7 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for MetaList { fn to_tokens(&self, tokens: &mut TokenStream) { self.path.to_tokens(tokens); @@ -644,6 +659,7 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for MetaNameValue { fn to_tokens(&self, tokens: &mut TokenStream) { self.path.to_tokens(tokens); diff --git a/src/custom_keyword.rs b/src/custom_keyword.rs index eb9726ede6..a21842efa9 100644 --- a/src/custom_keyword.rs +++ b/src/custom_keyword.rs @@ -91,23 +91,23 @@ macro_rules! custom_keyword { ($ident:ident) => { #[allow(non_camel_case_types)] pub struct $ident { - pub span: $crate::export::Span, + pub span: $crate::__private::Span, } #[doc(hidden)] #[allow(dead_code, non_snake_case)] - pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>( + pub fn $ident<__S: $crate::__private::IntoSpans<[$crate::__private::Span; 1]>>( span: __S, ) -> $ident { $ident { - span: $crate::export::IntoSpans::into_spans(span)[0], + span: $crate::__private::IntoSpans::into_spans(span)[0], } } - impl $crate::export::Default for $ident { + impl $crate::__private::Default for $ident { fn default() -> Self { $ident { - span: $crate::export::Span::call_site(), + span: $crate::__private::Span::call_site(), } } } @@ -127,7 +127,7 @@ macro_rules! impl_parse_for_custom_keyword { ($ident:ident) => { // For peek. impl $crate::token::CustomToken for $ident { - fn peek(cursor: $crate::buffer::Cursor) -> $crate::export::bool { + fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool { if let Some((ident, _rest)) = cursor.ident() { ident == stringify!($ident) } else { @@ -135,7 +135,7 @@ macro_rules! impl_parse_for_custom_keyword { } } - fn display() -> &'static $crate::export::str { + fn display() -> &'static $crate::__private::str { concat!("`", stringify!($ident), "`") } } @@ -143,12 +143,12 @@ macro_rules! impl_parse_for_custom_keyword { impl $crate::parse::Parse for $ident { fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { input.step(|cursor| { - if let $crate::export::Some((ident, rest)) = cursor.ident() { + if let $crate::__private::Some((ident, rest)) = cursor.ident() { if ident == stringify!($ident) { - return $crate::export::Ok(($ident { span: ident.span() }, rest)); + return $crate::__private::Ok(($ident { span: ident.span() }, rest)); } } - $crate::export::Err(cursor.error(concat!( + $crate::__private::Err(cursor.error(concat!( "expected `", stringify!($ident), "`" @@ -173,10 +173,10 @@ macro_rules! impl_parse_for_custom_keyword { #[macro_export] macro_rules! impl_to_tokens_for_custom_keyword { ($ident:ident) => { - impl $crate::export::ToTokens for $ident { - fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) { + impl $crate::__private::ToTokens for $ident { + fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { let ident = $crate::Ident::new(stringify!($ident), self.span); - $crate::export::TokenStreamExt::append(tokens, ident); + $crate::__private::TokenStreamExt::append(tokens, ident); } } }; @@ -196,9 +196,9 @@ macro_rules! impl_to_tokens_for_custom_keyword { #[macro_export] macro_rules! impl_clone_for_custom_keyword { ($ident:ident) => { - impl $crate::export::Copy for $ident {} + impl $crate::__private::Copy for $ident {} - impl $crate::export::Clone for $ident { + impl $crate::__private::Clone for $ident { fn clone(&self) -> Self { *self } @@ -220,25 +220,25 @@ macro_rules! impl_clone_for_custom_keyword { #[macro_export] macro_rules! impl_extra_traits_for_custom_keyword { ($ident:ident) => { - impl $crate::export::Debug for $ident { - fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result { - $crate::export::Formatter::write_str( + impl $crate::__private::Debug for $ident { + fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::fmt::Result { + $crate::__private::Formatter::write_str( f, concat!("Keyword [", stringify!($ident), "]"), ) } } - impl $crate::export::Eq for $ident {} + impl $crate::__private::Eq for $ident {} - impl $crate::export::PartialEq for $ident { - fn eq(&self, _other: &Self) -> $crate::export::bool { + impl $crate::__private::PartialEq for $ident { + fn eq(&self, _other: &Self) -> $crate::__private::bool { true } } - impl $crate::export::Hash for $ident { - fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {} + impl $crate::__private::Hash for $ident { + fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} } }; } diff --git a/src/custom_punctuation.rs b/src/custom_punctuation.rs index 2156ec2c64..128185da01 100644 --- a/src/custom_punctuation.rs +++ b/src/custom_punctuation.rs @@ -83,18 +83,18 @@ macro_rules! custom_punctuation { #[doc(hidden)] #[allow(dead_code, non_snake_case)] - pub fn $ident<__S: $crate::export::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>( + pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>( spans: __S, ) -> $ident { let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*; $ident { - spans: $crate::export::IntoSpans::into_spans(spans) + spans: $crate::__private::IntoSpans::into_spans(spans) } } - impl $crate::export::Default for $ident { + impl $crate::__private::Default for $ident { fn default() -> Self { - $ident($crate::export::Span::call_site()) + $ident($crate::__private::Span::call_site()) } } @@ -116,7 +116,7 @@ macro_rules! impl_parse_for_custom_punctuation { $crate::token::parsing::peek_punct(cursor, $crate::stringify_punct!($($tt)+)) } - fn display() -> &'static $crate::export::str { + fn display() -> &'static $crate::__private::str { concat!("`", $crate::stringify_punct!($($tt)+), "`") } } @@ -145,8 +145,8 @@ macro_rules! impl_parse_for_custom_punctuation { #[macro_export] macro_rules! impl_to_tokens_for_custom_punctuation { ($ident:ident, $($tt:tt)+) => { - impl $crate::export::ToTokens for $ident { - fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) { + impl $crate::__private::ToTokens for $ident { + fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { $crate::token::printing::punct($crate::stringify_punct!($($tt)+), &self.spans, tokens) } } @@ -167,9 +167,9 @@ macro_rules! impl_to_tokens_for_custom_punctuation { #[macro_export] macro_rules! impl_clone_for_custom_punctuation { ($ident:ident, $($tt:tt)+) => { - impl $crate::export::Copy for $ident {} + impl $crate::__private::Copy for $ident {} - impl $crate::export::Clone for $ident { + impl $crate::__private::Clone for $ident { fn clone(&self) -> Self { *self } @@ -191,22 +191,22 @@ macro_rules! impl_clone_for_custom_punctuation { #[macro_export] macro_rules! impl_extra_traits_for_custom_punctuation { ($ident:ident, $($tt:tt)+) => { - impl $crate::export::Debug for $ident { - fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result { - $crate::export::Formatter::write_str(f, stringify!($ident)) + impl $crate::__private::Debug for $ident { + fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::fmt::Result { + $crate::__private::Formatter::write_str(f, stringify!($ident)) } } - impl $crate::export::Eq for $ident {} + impl $crate::__private::Eq for $ident {} - impl $crate::export::PartialEq for $ident { - fn eq(&self, _other: &Self) -> $crate::export::bool { + impl $crate::__private::PartialEq for $ident { + fn eq(&self, _other: &Self) -> $crate::__private::bool { true } } - impl $crate::export::Hash for $ident { - fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {} + impl $crate::__private::Hash for $ident { + fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} } }; } @@ -224,7 +224,7 @@ macro_rules! impl_extra_traits_for_custom_punctuation { #[macro_export] macro_rules! custom_punctuation_repr { ($($tt:tt)+) => { - [$crate::export::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+] + [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+] }; } diff --git a/src/data.rs b/src/data.rs index bb6a854f44..731f5a0a7e 100644 --- a/src/data.rs +++ b/src/data.rs @@ -6,6 +6,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or `"full"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct Variant { /// Attributes tagged on the variant. pub attrs: Vec, @@ -31,10 +32,8 @@ ast_enum_of_structs! { /// /// This type is a [syntax tree enum]. /// - /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums - // - // TODO: change syntax-tree-enum link to an intra rustdoc link, currently - // blocked on https://github.com/rust-lang/rust/issues/62833 + /// [syntax tree enum]: Expr#syntax-tree-enums + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum Fields { /// Named fields of a struct or struct variant such as `Point { x: f64, /// y: f64 }`. @@ -54,6 +53,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct FieldsNamed { pub brace_token: token::Brace, pub named: Punctuated, @@ -65,6 +65,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct FieldsUnnamed { pub paren_token: token::Paren, pub unnamed: Punctuated, @@ -149,6 +150,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or `"full"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct Field { /// Attributes tagged on the field. pub attrs: Vec, @@ -179,10 +181,8 @@ ast_enum_of_structs! { /// /// This type is a [syntax tree enum]. /// - /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums - // - // TODO: change syntax-tree-enum link to an intra rustdoc link, currently - // blocked on https://github.com/rust-lang/rust/issues/62833 + /// [syntax tree enum]: Expr#syntax-tree-enums + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum Visibility { /// A public visibility level: `pub`. Public(VisPublic), @@ -204,6 +204,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct VisPublic { pub pub_token: Token![pub], } @@ -214,6 +215,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct VisCrate { pub crate_token: Token![crate], } @@ -225,6 +227,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct VisRestricted { pub pub_token: Token![pub], pub paren_token: token::Paren, @@ -240,6 +243,7 @@ pub mod parsing { use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseStream, Result}; + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Variant { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -269,6 +273,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for FieldsNamed { fn parse(input: ParseStream) -> Result { let content; @@ -279,6 +284,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for FieldsUnnamed { fn parse(input: ParseStream) -> Result { let content; @@ -291,6 +297,7 @@ pub mod parsing { impl Field { /// Parses a named (braced struct) field. + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_named(input: ParseStream) -> Result { Ok(Field { attrs: input.call(Attribute::parse_outer)?, @@ -302,6 +309,7 @@ pub mod parsing { } /// Parses an unnamed (tuple struct) field. + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_unnamed(input: ParseStream) -> Result { Ok(Field { attrs: input.call(Attribute::parse_outer)?, @@ -313,6 +321,7 @@ pub mod parsing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Visibility { fn parse(input: ParseStream) -> Result { // Recognize an empty None-delimited group, as produced by a $:vis @@ -408,6 +417,7 @@ mod printing { use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Variant { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); @@ -420,6 +430,7 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for FieldsNamed { fn to_tokens(&self, tokens: &mut TokenStream) { self.brace_token.surround(tokens, |tokens| { @@ -428,6 +439,7 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for FieldsUnnamed { fn to_tokens(&self, tokens: &mut TokenStream) { self.paren_token.surround(tokens, |tokens| { @@ -436,6 +448,7 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Field { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); @@ -448,18 +461,21 @@ mod printing { } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for VisPublic { fn to_tokens(&self, tokens: &mut TokenStream) { self.pub_token.to_tokens(tokens) } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for VisCrate { fn to_tokens(&self, tokens: &mut TokenStream) { self.crate_token.to_tokens(tokens); } } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for VisRestricted { fn to_tokens(&self, tokens: &mut TokenStream) { self.pub_token.to_tokens(tokens); diff --git a/src/derive.rs b/src/derive.rs index cbfd0da54a..af9bb91b7a 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -5,6 +5,7 @@ ast_struct! { /// Data structure sent to a `proc_macro_derive` macro. /// /// *This type is available only if Syn is built with the `"derive"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] pub struct DeriveInput { /// Attributes tagged on the whole struct or enum. pub attrs: Vec, @@ -32,10 +33,8 @@ ast_enum_of_structs! { /// /// This type is a [syntax tree enum]. /// - /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums - // - // TODO: change syntax-tree-enum link to an intra rustdoc link, currently - // blocked on https://github.com/rust-lang/rust/issues/62833 + /// [syntax tree enum]: Expr#syntax-tree-enums + #[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] pub enum Data { /// A struct input to a `proc_macro_derive` macro. Struct(DataStruct), @@ -55,6 +54,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] pub struct DataStruct { pub struct_token: Token![struct], pub fields: Fields, @@ -67,6 +67,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] pub struct DataEnum { pub enum_token: Token![enum], pub brace_token: token::Brace, @@ -79,6 +80,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` /// feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] pub struct DataUnion { pub union_token: Token![union], pub fields: FieldsNamed, @@ -90,6 +92,7 @@ pub mod parsing { use super::*; use crate::parse::{Parse, ParseStream, Result}; + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for DeriveInput { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -225,6 +228,7 @@ mod printing { use proc_macro2::TokenStream; use quote::ToTokens; + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for DeriveInput { fn to_tokens(&self, tokens: &mut TokenStream) { for attr in self.attrs.outer() { diff --git a/src/error.rs b/src/error.rs index 8d65a1b416..6051f3b686 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,7 +23,7 @@ pub type Result = std::result::Result; /// [`compile_error!`] in the generated code. This produces a better diagnostic /// message than simply panicking the macro. /// -/// [`compile_error!`]: https://doc.rust-lang.org/std/macro.compile_error.html +/// [`compile_error!`]: std::compile_error! /// /// When parsing macro input, the [`parse_macro_input!`] macro handles the /// conversion to `compile_error!` automatically. @@ -189,7 +189,7 @@ impl Error { /// The [`parse_macro_input!`] macro provides a convenient way to invoke /// this method correctly in a procedural macro. /// - /// [`compile_error!`]: https://doc.rust-lang.org/std/macro.compile_error.html + /// [`compile_error!`]: std::compile_error! pub fn to_compile_error(&self) -> TokenStream { self.messages .iter() @@ -197,6 +197,42 @@ impl Error { .collect() } + /// Render the error as an invocation of [`compile_error!`]. + /// + /// [`compile_error!`]: std::compile_error! + /// + /// # Example + /// + /// ``` + /// # extern crate proc_macro; + /// # + /// use proc_macro::TokenStream; + /// use syn::{parse_macro_input, DeriveInput, Error}; + /// + /// # const _: &str = stringify! { + /// #[proc_macro_derive(MyTrait)] + /// # }; + /// pub fn derive_my_trait(input: TokenStream) -> TokenStream { + /// let input = parse_macro_input!(input as DeriveInput); + /// my_trait::expand(input) + /// .unwrap_or_else(Error::into_compile_error) + /// .into() + /// } + /// + /// mod my_trait { + /// use proc_macro2::TokenStream; + /// use syn::{DeriveInput, Result}; + /// + /// pub(crate) fn expand(input: DeriveInput) -> Result { + /// /* ... */ + /// # unimplemented!() + /// } + /// } + /// ``` + pub fn into_compile_error(self) -> TokenStream { + self.to_compile_error() + } + /// Add another error message to self such that when `to_compile_error()` is /// called, both errors will be emitted together. pub fn combine(&mut self, another: Error) { @@ -309,11 +345,7 @@ impl Clone for ErrorMessage { } } -impl std::error::Error for Error { - fn description(&self) -> &str { - "parse error" - } -} +impl std::error::Error for Error {} impl From for Error { fn from(err: LexError) -> Self { diff --git a/src/export.rs b/src/export.rs index 37dc467a7f..601a214b1e 100644 --- a/src/export.rs +++ b/src/export.rs @@ -33,3 +33,5 @@ mod help { pub type Bool = bool; pub type Str = str; } + +pub struct private(pub(crate) ()); diff --git a/src/expr.rs b/src/expr.rs index 45dd0ce347..510592de26 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -86,6 +86,7 @@ ast_enum_of_structs! { /// A sign that you may not be choosing the right variable names is if you /// see names getting repeated in your code, like accessing /// `receiver.receiver` or `pat.pat` or `cond.cond`. + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub enum Expr { /// A slice literal expression: `[a, b, c, d]`. Array(ExprArray), @@ -223,8 +224,31 @@ 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. + // + // Once `deny(reachable)` is available in rustc, Expr will be + // reimplemented as a non_exhaustive enum. + // https://github.com/rust-lang/rust/issues/44109#issuecomment-521781237 #[doc(hidden)] - __Nonexhaustive, + __TestExhaustive(crate::private), } } @@ -232,6 +256,7 @@ ast_struct! { /// A slice literal expression: `[a, b, c, d]`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprArray #full { pub attrs: Vec, pub bracket_token: token::Bracket, @@ -243,6 +268,7 @@ ast_struct! { /// An assignment expression: `a = compute()`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprAssign #full { pub attrs: Vec, pub left: Box, @@ -255,6 +281,7 @@ ast_struct! { /// A compound assignment expression: `counter += 1`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprAssignOp #full { pub attrs: Vec, pub left: Box, @@ -267,6 +294,7 @@ ast_struct! { /// An async block: `async { ... }`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprAsync #full { pub attrs: Vec, pub async_token: Token![async], @@ -279,6 +307,7 @@ ast_struct! { /// An await expression: `fut.await`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprAwait #full { pub attrs: Vec, pub base: Box, @@ -292,6 +321,7 @@ ast_struct! { /// /// *This type is available only if Syn is built with the `"derive"` or /// `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] pub struct ExprBinary { pub attrs: Vec, pub left: Box, @@ -304,6 +334,7 @@ ast_struct! { /// A blocked scope: `{ ... }`. /// /// *This type is available only if Syn is built with the `"full"` feature.* + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct ExprBlock #full { pub attrs: Vec, pub label: Option