From 336bed4254accdcdd59f8732a576a5c91c046f02 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Wed, 15 Nov 2023 18:26:11 +0000 Subject: Upgrade syn-mid to 0.6.0 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/syn-mid For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md Test: TreeHugger Change-Id: Iaa98b0ba25d99f31d5a5551ccfea620af484e94f --- .cargo_vcs_info.json | 7 +- .clippy.toml | 1 - .editorconfig | 20 ---- .gitattributes | 1 - .rustfmt.toml | 34 ------ Android.bp | 6 +- CHANGELOG.md | 20 +++- Cargo.toml | 50 ++++++--- Cargo.toml.orig | 24 +++-- METADATA | 14 ++- README.md | 28 +++-- patches/syn-2.patch | 50 --------- src/func.rs | 297 +++++++++++++++++++++++++++++++++++++-------------- src/lib.rs | 106 ++++++++++++------ src/macros.rs | 15 +-- src/pat.rs | 204 ++++++++++++++++++++--------------- src/path.rs | 4 + 17 files changed, 513 insertions(+), 368 deletions(-) delete mode 100644 .clippy.toml delete mode 100644 .editorconfig delete mode 100644 .gitattributes delete mode 100644 .rustfmt.toml delete mode 100644 patches/syn-2.patch diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index ea91f75..4d497af 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "6a4dcb4b7c3bcb2ad086745bc88515a6756e3edc" - } -} + "sha1": "4eebddd1dc907b1edb4deb1b497617698a086f12" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/.clippy.toml b/.clippy.toml deleted file mode 100644 index 422beeb..0000000 --- a/.clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.31" diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a73a88d..0000000 --- a/.editorconfig +++ /dev/null @@ -1,20 +0,0 @@ -# EditorConfig configuration -# https://editorconfig.org - -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -[*.{json,yml,md}] -indent_size = 2 - -[*.sh] -indent_size = 2 -binary_next_line = true -switch_case_indent = true diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 6313b56..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto eol=lf diff --git a/.rustfmt.toml b/.rustfmt.toml deleted file mode 100644 index 6604f5c..0000000 --- a/.rustfmt.toml +++ /dev/null @@ -1,34 +0,0 @@ -# Rustfmt configuration -# https://github.com/rust-lang/rustfmt/blob/HEAD/Configurations.md - -# This is required for bug-fixes, which technically can't be made to the stable -# first version. -# This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3383). -version = "Two" -# Rustfmt cannot format long lines inside macros, but this option detects this. -# This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3391) -error_on_line_overflow = true - -# Override the default formatting style. -# See https://internals.rust-lang.org/t/running-rustfmt-on-rust-lang-rust-and-other-rust-lang-repositories/8732/81. -use_small_heuristics = "Max" -# See https://github.com/rust-dev-tools/fmt-rfcs/issues/149. -# This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3370) -overflow_delimited_expr = true - -# Apply rustfmt to more places. -# This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3362). -merge_imports = true -# This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3348). -format_code_in_doc_comments = true - -# Automatically fix deprecated style. -use_field_init_shorthand = true -use_try_shorthand = true - -# Set the default settings again to always apply the proper formatting without -# being affected by the editor settings. -edition = "2018" -hard_tabs = false -newline_style = "Unix" -tab_spaces = 4 diff --git a/Android.bp b/Android.bp index 5a487de..f1a600f 100644 --- a/Android.bp +++ b/Android.bp @@ -41,15 +41,13 @@ rust_library_host { name: "libsyn_mid", crate_name: "syn_mid", cargo_env_compat: true, - cargo_pkg_version: "0.5.3", + cargo_pkg_version: "0.6.0", srcs: ["src/lib.rs"], - edition: "2018", + edition: "2021", rustlibs: [ "libproc_macro2", "libquote", "libsyn", ], compile_multilib: "first", - product_available: true, - vendor_available: true, } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eafa2e..82ee4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +## [0.6.0] - 2023-09-30 + +- Update to syn 2.0. ([#26](https://github.com/taiki-e/syn-mid/pull/26)) + +## [0.5.4] - 2023-06-29 + +- Increase the minimum supported Rust version from Rust 1.31 to Rust 1.56. + +- Update minimal version of `proc-macro2` to 1.0.60. + ## [0.5.3] - 2021-01-05 - Exclude unneeded files from crates.io. @@ -28,9 +38,7 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [0.5.0] - 2019-12-09 -- [Added `Signature` type.][13] - -[13]: https://github.com/taiki-e/syn-mid/pull/13 +- Added `Signature` type. ([#13](https://github.com/taiki-e/syn-mid/pull/13)) ## [0.4.0] - 2019-08-15 @@ -62,11 +70,13 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [0.1.0] - 2019-02-14 -**Note: This release has been yanked.** +**Note:** This release has been yanked. Initial release -[Unreleased]: https://github.com/taiki-e/syn-mid/compare/v0.5.3...HEAD +[Unreleased]: https://github.com/taiki-e/syn-mid/compare/v0.6.0...HEAD +[0.6.0]: https://github.com/taiki-e/syn-mid/compare/v0.5.4...v0.6.0 +[0.5.4]: https://github.com/taiki-e/syn-mid/compare/v0.5.3...v0.5.4 [0.5.3]: https://github.com/taiki-e/syn-mid/compare/v0.5.2...v0.5.3 [0.5.2]: https://github.com/taiki-e/syn-mid/compare/v0.5.1...v0.5.2 [0.5.1]: https://github.com/taiki-e/syn-mid/compare/v0.5.0...v0.5.1 diff --git a/Cargo.toml b/Cargo.toml index 422755b..a646f02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,31 +3,45 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" +rust-version = "1.56" name = "syn-mid" -version = "0.5.3" -authors = ["Taiki Endo "] -exclude = ["/.github", "/scripts"] -description = "Providing the features between \"full\" and \"derive\" of syn.\n" -documentation = "https://docs.rs/syn-mid" +version = "0.6.0" +exclude = [ + "/.*", + "/tools", +] +description = """ +Providing the features between \"full\" and \"derive\" of syn. +""" readme = "README.md" -keywords = ["syn", "macros"] -categories = ["development-tools::procedural-macro-helpers"] +keywords = [ + "syn", + "macros", +] +categories = [ + "development-tools::procedural-macro-helpers", + "parser-implementations", +] license = "Apache-2.0 OR MIT" repository = "https://github.com/taiki-e/syn-mid" + [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] + +[lib] +doc-scrape-examples = false + [dependencies.proc-macro2] -version = "1" +version = "1.0.60" default-features = false [dependencies.quote] @@ -35,8 +49,12 @@ version = "1" default-features = false [dependencies.syn] -version = "1.0.44" -features = ["parsing", "printing", "derive"] +version = "2" +features = [ + "parsing", + "printing", + "derive", +] default-features = false [features] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 965c63a..0c7caa0 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,15 +1,13 @@ [package] name = "syn-mid" -version = "0.5.3" -authors = ["Taiki Endo "] -edition = "2018" +version = "0.6.0" +edition = "2021" +rust-version = "1.56" license = "Apache-2.0 OR MIT" repository = "https://github.com/taiki-e/syn-mid" -documentation = "https://docs.rs/syn-mid" keywords = ["syn", "macros"] -categories = ["development-tools::procedural-macro-helpers"] -readme = "README.md" -exclude = ["/.github", "/scripts"] +categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] +exclude = ["/.*", "/tools"] description = """ Providing the features between \"full\" and \"derive\" of syn. """ @@ -19,13 +17,17 @@ all-features = true targets = ["x86_64-unknown-linux-gnu"] [workspace] -members = ["examples/const_fn", "examples/const_fn_test"] +resolver = "2" +members = ["examples/const_fn"] + +[lib] +doc-scrape-examples = false [features] clone-impls = ["syn/clone-impls"] -# NB: proc-macro2, quote, and syn are public dependencies. +# Note: proc-macro2, quote, and syn are public dependencies. [dependencies] -proc-macro2 = { version = "1", default-features = false } +proc-macro2 = { version = "1.0.60", default-features = false } quote = { version = "1", default-features = false } -syn = { version = "1.0.44", default-features = false, features = ["parsing", "printing", "derive"] } +syn = { version = "2", default-features = false, features = ["parsing", "printing", "derive"] } diff --git a/METADATA b/METADATA index 540870e..df66819 100644 --- a/METADATA +++ b/METADATA @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/syn-mid +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md + name: "syn-mid" description: "Providing the features between \"full\" and \"derive\" of syn." third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/syn-mid/syn-mid-0.5.3.crate" + value: "https://static.crates.io/crates/syn-mid/syn-mid-0.6.0.crate" } - version: "0.5.3" + version: "0.6.0" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 1 - day: 5 + year: 2023 + month: 11 + day: 15 } } diff --git a/README.md b/README.md index 87ce628..cfd6f7e 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,24 @@ # syn-mid -[![crates.io](https://img.shields.io/crates/v/syn-mid.svg?style=flat-square&logo=rust)](https://crates.io/crates/syn-mid) -[![docs.rs](https://img.shields.io/badge/docs.rs-syn--mid-blue?style=flat-square)](https://docs.rs/syn-mid) -[![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg?style=flat-square)](#license) -[![rustc](https://img.shields.io/badge/rustc-1.31+-blue.svg?style=flat-square)](https://www.rust-lang.org) -[![build status](https://img.shields.io/github/workflow/status/taiki-e/syn-mid/CI/master?style=flat-square)](https://github.com/taiki-e/syn-mid/actions?query=workflow%3ACI+branch%3Amaster) +[![crates.io](https://img.shields.io/crates/v/syn-mid?style=flat-square&logo=rust)](https://crates.io/crates/syn-mid) +[![docs.rs](https://img.shields.io/badge/docs.rs-syn--mid-blue?style=flat-square&logo=docs.rs)](https://docs.rs/syn-mid) +[![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue?style=flat-square)](#license) +[![rustc](https://img.shields.io/badge/rustc-1.56+-blue?style=flat-square&logo=rust)](https://www.rust-lang.org) +[![build status](https://img.shields.io/github/actions/workflow/status/taiki-e/syn-mid/ci.yml?branch=main&style=flat-square&logo=github)](https://github.com/taiki-e/syn-mid/actions) + Providing the features between "full" and "derive" of syn. This crate provides the following two unique data structures. -- `syn_mid::ItemFn` -- A function whose body is not parsed. +- [`syn_mid::ItemFn`] -- A function whose body is not parsed. ```text fn process(n: usize) -> Result<()> { ... } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^ ``` -- `syn_mid::Block` -- A block whose body is not parsed. +- [`syn_mid::Block`] -- A block whose body is not parsed. ```text { ... } @@ -28,8 +29,6 @@ Other data structures are the same as data structures of [syn]. These are defined in this crate because they cannot be used in [syn] without "full" feature. -[syn]: https://github.com/dtolnay/syn - ## Usage Add this to your `Cargo.toml`: @@ -39,14 +38,21 @@ Add this to your `Cargo.toml`: syn-mid = "0.5" ``` -*Compiler support: requires rustc 1.31+* +*Compiler support: requires rustc 1.56+* -[**Examples**](examples) +[**Examples**](https://github.com/taiki-e/syn-mid/tree/HEAD/examples) ## Optional features - **`clone-impls`** — Clone impls for all syntax tree types. +[syn]: https://github.com/dtolnay/syn + + + +[`syn_mid::Block`]: https://docs.rs/syn-mid/latest/syn_mid/struct.Block.html +[`syn_mid::ItemFn`]: https://docs.rs/syn-mid/latest/syn_mid/struct.ItemFn.html + ## License Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or diff --git a/patches/syn-2.patch b/patches/syn-2.patch deleted file mode 100644 index a25bd0c..0000000 --- a/patches/syn-2.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/src/func.rs b/src/func.rs -index a53e63b..384e80b 100644 ---- a/src/func.rs -+++ b/src/func.rs -@@ -85,11 +85,12 @@ mod parsing { - - impl Parse for Signature { - fn parse(input: ParseStream<'_>) -> Result { -+ #[allow(clippy::trivially_copy_pass_by_ref)] - fn get_variadic(input: &&FnArg) -> Option { - if let FnArg::Typed(PatType { ty, .. }) = input { - if let Type::Verbatim(tokens) = &**ty { - if let Ok(dots) = parse2(tokens.clone()) { -- return Some(Variadic { attrs: Vec::new(), dots }); -+ return Some(Variadic { attrs: Vec::new(), pat: None, comma: None, dots }); - } - } - } -@@ -106,7 +107,7 @@ mod parsing { - - let content; - let paren_token = parenthesized!(content in input); -- let inputs = content.parse_terminated(FnArg::parse)?; -+ let inputs = content.parse_terminated(FnArg::parse, Token![,])?; - let variadic = inputs.last().as_ref().and_then(get_variadic); - - let output: ReturnType = input.parse()?; -@@ -119,11 +120,11 @@ mod parsing { - abi, - fn_token, - ident, -+ generics, - paren_token, - inputs, -- output, - variadic, -- generics, -+ output, - }) - } - } -@@ -210,7 +211,7 @@ mod printing { - FnArg::Receiver(_) => return false, - }; - -- let tokens = match pat.ty.as_ref() { -+ let tokens = match &*pat.ty { - Type::Verbatim(tokens) => tokens, - _ => return false, - }; diff --git a/src/func.rs b/src/func.rs index 384e80b..477841b 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,10 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Based on https://github.com/dtolnay/syn/blob/2.0.37/src/item.rs. + use proc_macro2::TokenStream; use syn::{ punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, Lifetime, ReturnType, Token, - Variadic, Visibility, + Type, Visibility, }; -use super::PatType; +use super::{Pat, PatType}; ast_struct! { /// A free-standing function: `fn process(n: usize) -> Result<()> { ... @@ -64,6 +68,18 @@ ast_struct! { pub reference: Option<(Token![&], Option)>, pub mutability: Option, pub self_token: Token![self], + pub colon_token: Option, + pub ty: Box, + } +} + +ast_struct! { + /// The variadic argument of a foreign function. + pub struct Variadic { + pub attrs: Vec, + pub pat: Option<(Box, Token![:])>, + pub dots: Token![...], + pub comma: Option, } } @@ -71,10 +87,13 @@ mod parsing { use syn::{ braced, parenthesized, parse::{discouraged::Speculative, Parse, ParseStream, Result}, - parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility, + punctuated::Punctuated, + Abi, Attribute, Error, Generics, Ident, Lifetime, Path, ReturnType, Token, Type, TypePath, + TypeReference, Visibility, }; - use super::{Block, FnArg, ItemFn, PatType, Receiver, Signature}; + use super::{Block, FnArg, ItemFn, Receiver, Signature, Variadic}; + use crate::pat::{Pat, PatType, PatWild}; impl Parse for Block { fn parse(input: ParseStream<'_>) -> Result { @@ -85,18 +104,6 @@ mod parsing { impl Parse for Signature { fn parse(input: ParseStream<'_>) -> Result { - #[allow(clippy::trivially_copy_pass_by_ref)] - fn get_variadic(input: &&FnArg) -> Option { - if let FnArg::Typed(PatType { ty, .. }) = input { - if let Type::Verbatim(tokens) = &**ty { - if let Ok(dots) = parse2(tokens.clone()) { - return Some(Variadic { attrs: Vec::new(), pat: None, comma: None, dots }); - } - } - } - None - } - let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; let unsafety: Option = input.parse()?; @@ -107,8 +114,7 @@ mod parsing { let content; let paren_token = parenthesized!(content in input); - let inputs = content.parse_terminated(FnArg::parse, Token![,])?; - let variadic = inputs.last().as_ref().and_then(get_variadic); + let (inputs, variadic) = parse_fn_args(&content)?; let output: ReturnType = input.parse()?; generics.where_clause = input.parse()?; @@ -133,7 +139,7 @@ mod parsing { fn parse(input: ParseStream<'_>) -> Result { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; - let sig = input.parse()?; + let sig: Signature = input.parse()?; let block = input.parse()?; Ok(Self { attrs, vis, sig, block: Box::new(block) }) } @@ -141,56 +147,185 @@ mod parsing { impl Parse for FnArg { fn parse(input: ParseStream<'_>) -> Result { + let allow_variadic = false; let attrs = input.call(Attribute::parse_outer)?; - - let ahead = input.fork(); - if let Ok(mut receiver) = ahead.parse::() { - if !ahead.peek(Token![:]) { - input.advance_to(&ahead); - receiver.attrs = attrs; - return Ok(FnArg::Receiver(receiver)); - } + match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? { + FnArgOrVariadic::FnArg(arg) => Ok(arg), + FnArgOrVariadic::Variadic(_) => unreachable!(), } + } + } + + enum FnArgOrVariadic { + FnArg(FnArg), + Variadic(Variadic), + } - let mut typed = input.call(fn_arg_typed)?; - typed.attrs = attrs; - Ok(FnArg::Typed(typed)) + fn parse_fn_arg_or_variadic( + input: ParseStream<'_>, + attrs: Vec, + allow_variadic: bool, + ) -> Result { + let ahead = input.fork(); + if let Ok(mut receiver) = ahead.parse::() { + input.advance_to(&ahead); + receiver.attrs = attrs; + return Ok(FnArgOrVariadic::FnArg(FnArg::Receiver(receiver))); } + + // Hack to parse pre-2018 syntax in + // test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs + // because the rest of the test case is valuable. + if input.peek(Ident) && input.peek2(Token![<]) { + let span = input.fork().parse::()?.span(); + return Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType { + attrs, + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), + colon_token: Token![:](span), + ty: input.parse()?, + }))); + } + + let pat = Box::new(Pat::parse_single(input)?); + let colon_token: Token![:] = input.parse()?; + + if allow_variadic { + if let Some(dots) = input.parse::>()? { + return Ok(FnArgOrVariadic::Variadic(Variadic { + attrs, + pat: Some((pat, colon_token)), + dots, + comma: None, + })); + } + } + + Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType { + attrs, + pat, + colon_token, + ty: input.parse()?, + }))) } impl Parse for Receiver { fn parse(input: ParseStream<'_>) -> Result { + let reference = if input.peek(Token![&]) { + let ampersand: Token![&] = input.parse()?; + let lifetime: Option = input.parse()?; + Some((ampersand, lifetime)) + } else { + None + }; + let mutability: Option = input.parse()?; + let self_token: Token![self] = input.parse()?; + let colon_token: Option = + if reference.is_some() { None } else { input.parse()? }; + let ty: Type = if colon_token.is_some() { + input.parse()? + } else { + let mut ty = Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("Self", self_token.span)), + }); + if let Some((ampersand, lifetime)) = reference.as_ref() { + ty = Type::Reference(TypeReference { + and_token: Token![&](ampersand.span), + lifetime: lifetime.clone(), + mutability: mutability.as_ref().map(|m| Token![mut](m.span)), + elem: Box::new(ty), + }); + } + ty + }; Ok(Self { attrs: Vec::new(), - reference: { - if input.peek(Token![&]) { - Some((input.parse()?, input.parse()?)) - } else { - None - } - }, - mutability: input.parse()?, - self_token: input.parse()?, + reference, + mutability, + self_token, + colon_token, + ty: Box::new(ty), }) } } - fn fn_arg_typed(input: ParseStream<'_>) -> Result { - Ok(PatType { - attrs: Vec::new(), - pat: input.parse()?, - colon_token: input.parse()?, - ty: Box::new(input.parse()?), - }) + fn parse_fn_args( + input: ParseStream<'_>, + ) -> Result<(Punctuated, Option)> { + let mut args = Punctuated::new(); + let mut variadic = None; + let mut has_receiver = false; + + while !input.is_empty() { + let attrs = input.call(Attribute::parse_outer)?; + + if let Some(dots) = input.parse::>()? { + variadic = Some(Variadic { + attrs, + pat: None, + dots, + comma: if input.is_empty() { None } else { Some(input.parse()?) }, + }); + break; + } + + let allow_variadic = true; + let arg = match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? { + FnArgOrVariadic::FnArg(arg) => arg, + FnArgOrVariadic::Variadic(arg) => { + variadic = Some(Variadic { + comma: if input.is_empty() { None } else { Some(input.parse()?) }, + ..arg + }); + break; + } + }; + + match &arg { + FnArg::Receiver(receiver) if has_receiver => { + return Err(Error::new( + receiver.self_token.span, + "unexpected second method receiver", + )); + } + FnArg::Receiver(receiver) if !args.is_empty() => { + return Err(Error::new(receiver.self_token.span, "unexpected method receiver")); + } + FnArg::Receiver(_) => has_receiver = true, + FnArg::Typed(_) => {} + } + args.push_value(arg); + + if input.is_empty() { + break; + } + + let comma: Token![,] = input.parse()?; + args.push_punct(comma); + } + + Ok((args, variadic)) } } mod printing { use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; - use syn::{punctuated::Punctuated, Token, Type}; + use syn::{Token, Type}; - use super::{Block, FnArg, ItemFn, Receiver, Signature}; + use super::{Block, ItemFn, Receiver, Signature, Variadic}; + + impl ToTokens for ItemFn { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.vis.to_tokens(tokens); + self.sig.to_tokens(tokens); + self.block.to_tokens(tokens); + } + } impl ToTokens for Block { fn to_tokens(&self, tokens: &mut TokenStream) { @@ -200,25 +335,6 @@ mod printing { } } - fn has_variadic(inputs: &Punctuated) -> bool { - let last = match inputs.last() { - Some(last) => last, - None => return false, - }; - - let pat = match last { - FnArg::Typed(pat) => pat, - FnArg::Receiver(_) => return false, - }; - - let tokens = match &*pat.ty { - Type::Verbatim(tokens) => tokens, - _ => return false, - }; - - tokens.to_string() == "..." - } - impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { self.constness.to_tokens(tokens); @@ -230,11 +346,11 @@ mod printing { self.generics.to_tokens(tokens); self.paren_token.surround(tokens, |tokens| { self.inputs.to_tokens(tokens); - if self.variadic.is_some() && !has_variadic(&self.inputs) { + if let Some(variadic) = &self.variadic { if !self.inputs.empty_or_trailing() { ::default().to_tokens(tokens); } - self.variadic.to_tokens(tokens); + variadic.to_tokens(tokens); } }); self.output.to_tokens(tokens); @@ -242,17 +358,6 @@ mod printing { } } - impl ToTokens for ItemFn { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.append_all(&self.attrs); - self.vis.to_tokens(tokens); - self.sig.to_tokens(tokens); - self.block.brace_token.surround(tokens, |tokens| { - tokens.append_all(self.block.stmts.clone()); - }); - } - } - impl ToTokens for Receiver { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); @@ -262,6 +367,38 @@ mod printing { } self.mutability.to_tokens(tokens); self.self_token.to_tokens(tokens); + if let Some(colon_token) = &self.colon_token { + colon_token.to_tokens(tokens); + self.ty.to_tokens(tokens); + } else { + let consistent = match (&self.reference, &self.mutability, &*self.ty) { + (Some(_), mutability, Type::Reference(ty)) => { + mutability.is_some() == ty.mutability.is_some() + && match &*ty.elem { + Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"), + _ => false, + } + } + (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"), + _ => false, + }; + if !consistent { + ::default().to_tokens(tokens); + self.ty.to_tokens(tokens); + } + } + } + } + + impl ToTokens for Variadic { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + if let Some((pat, colon)) = &self.pat { + pat.to_tokens(tokens); + colon.to_tokens(tokens); + } + self.dots.to_tokens(tokens); + self.comma.to_tokens(tokens); } } } diff --git a/src/lib.rs b/src/lib.rs index ca490cf..ad0bf73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,32 +1,50 @@ -//! Providing the features between "full" and "derive" of syn. -//! -//! This crate provides the following two unique data structures. -//! -//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed. -//! -//! ```text -//! fn process(n: usize) -> Result<()> { ... } -//! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^ -//! ``` -//! -//! * [`syn_mid::Block`] -- A block whose body is not parsed. -//! -//! ```text -//! { ... } -//! ^ ^ -//! ``` -//! -//! Other data structures are the same as data structures of [syn]. These are -//! defined in this crate because they cannot be used in [syn] without "full" -//! feature. -//! -//! # Optional features -//! -//! * **`clone-impls`** — Clone impls for all syntax tree types. -//! -//! [`syn_mid::ItemFn`]: ItemFn -//! [`syn_mid::Block`]: Block -//! [syn]: https://github.com/dtolnay/syn +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/*! + +Providing the features between "full" and "derive" of syn. + +This crate provides the following two unique data structures. + +- [`syn_mid::ItemFn`] -- A function whose body is not parsed. + + ```text + fn process(n: usize) -> Result<()> { ... } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^ + ``` + +- [`syn_mid::Block`] -- A block whose body is not parsed. + + ```text + { ... } + ^ ^ + ``` + +Other data structures are the same as data structures of [syn]. These are +defined in this crate because they cannot be used in [syn] without "full" +feature. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +syn-mid = "0.5" +``` + +*Compiler support: requires rustc 1.56+* + +[**Examples**](https://github.com/taiki-e/syn-mid/tree/HEAD/examples) + +## Optional features + +- **`clone-impls`** — Clone impls for all syntax tree types. + +[syn]: https://github.com/dtolnay/syn + + +*/ #![doc(test( no_crate_inject, @@ -36,12 +54,29 @@ ) ))] #![forbid(unsafe_code)] -#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)] -#![warn(clippy::all, clippy::default_trait_access)] -#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)] +#![warn( + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub, + clippy::pedantic, + // Lints that may help when writing public library. + // missing_debug_implementations, + // missing_docs, + clippy::alloc_instead_of_core, + // clippy::exhaustive_enums, // TODO + // clippy::exhaustive_structs, // TODO + clippy::impl_trait_in_params, + // clippy::missing_inline_in_public_items, + // clippy::std_instead_of_alloc, + clippy::std_instead_of_core, +)] +#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)] // Many of the code contained in this crate are copies from https://github.com/dtolnay/syn. +#[cfg(doc)] +extern crate self as syn_mid; + #[macro_use] mod macros; @@ -49,10 +84,13 @@ mod func; mod pat; mod path; +#[doc(no_inline)] +pub use syn::ExprPath as PatPath; + pub use crate::{ - func::{Block, FnArg, ItemFn, Receiver, Signature}, + func::{Block, FnArg, ItemFn, Receiver, Signature, Variadic}, pat::{ - FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, + FieldPat, Pat, PatIdent, PatReference, PatRest, PatStruct, PatTuple, PatTupleStruct, PatType, PatWild, }, }; diff --git a/src/macros.rs b/src/macros.rs index 87be7b4..7ea574c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + macro_rules! ast_struct { ( [$($attrs_pub:tt)*] @@ -7,8 +9,8 @@ macro_rules! ast_struct { $($attrs_pub)* struct $name $($rest)* }; - ($($t:tt)*) => { - strip_attrs_pub!(ast_struct!($($t)*)); + ($($tt:tt)*) => { + strip_attrs_pub!(ast_struct!($($tt)*)); }; } @@ -21,8 +23,8 @@ macro_rules! ast_enum { $($attrs_pub)* enum $name $($rest)* ); - ($($t:tt)*) => { - strip_attrs_pub!(ast_enum!($($t)*)); + ($($tt:tt)*) => { + strip_attrs_pub!(ast_enum!($($tt)*)); }; } @@ -93,15 +95,14 @@ macro_rules! generate_to_tokens { } macro_rules! strip_attrs_pub { - ($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => { + ($mac:ident!($(#[$m:meta])* $pub:ident $($tt:tt)*)) => { check_keyword_matches!(pub $pub); - $mac!([$(#[$m])* $pub] $($t)*); + $mac!([$(#[$m])* $pub] $($tt)*); }; } macro_rules! check_keyword_matches { - (struct struct) => {}; (enum enum) => {}; (pub pub) => {}; } diff --git a/src/pat.rs b/src/pat.rs index 78bf890..7a193fb 100644 --- a/src/pat.rs +++ b/src/pat.rs @@ -1,14 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Based on https://github.com/dtolnay/syn/blob/2.0.37/src/item.rs. + use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type}; +use super::PatPath; + ast_enum_of_structs! { /// A pattern in a local binding, function signature, match expression, or /// various other places. - /// - /// # Syntax tree enum - /// - /// This type is a [syntax tree enum]. - /// - /// [syntax tree enum]: https://docs.rs/syn/1/syn/enum.Expr.html#syntax-tree-enums + #[non_exhaustive] pub enum Pat { /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. Ident(PatIdent), @@ -33,9 +34,6 @@ ast_enum_of_structs! { /// A pattern that matches any value: `_`. Wild(PatWild), - - #[doc(hidden)] - __Nonexhaustive, } } @@ -49,14 +47,6 @@ ast_struct! { } } -ast_struct! { - /// A path pattern like `Color::Red`. - pub struct PatPath { - pub attrs: Vec, - pub path: Path, - } -} - ast_struct! { /// A reference pattern: `&mut var`. pub struct PatReference { @@ -67,6 +57,14 @@ ast_struct! { } } +ast_struct! { + /// The dots in a tuple pattern: `[0, 1, ..]`. + pub struct PatRest { + pub attrs: Vec, + pub dot2_token: Token![..], + } +} + ast_struct! { /// A struct or struct variant pattern: `Variant { x, y, .. }`. pub struct PatStruct { @@ -74,7 +72,7 @@ ast_struct! { pub path: Path, pub brace_token: token::Brace, pub fields: Punctuated, - pub dot2_token: Option, + pub rest: Option, } } @@ -92,7 +90,8 @@ ast_struct! { pub struct PatTupleStruct { pub attrs: Vec, pub path: Path, - pub pat: PatTuple, + pub paren_token: token::Paren, + pub elems: Punctuated, } } @@ -132,34 +131,32 @@ mod parsing { braced, ext::IdentExt, parenthesized, - parse::{Parse, ParseStream, Result}, + parse::{ParseStream, Result}, punctuated::Punctuated, - token, Attribute, Ident, Member, Path, Token, + token, Attribute, ExprPath, Ident, Member, Path, Token, }; use super::{ - FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, + FieldPat, Pat, PatIdent, PatReference, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, }; use crate::path; - impl Parse for Pat { - fn parse(input: ParseStream<'_>) -> Result { + impl Pat { + /// Parse a pattern that does _not_ involve `|` at the top level. + pub fn parse_single(input: ParseStream<'_>) -> Result { let lookahead = input.lookahead1(); - if { - let ahead = input.fork(); - ahead.parse::>()?.is_some() - && (ahead.peek(Token![::]) - || ahead.peek(token::Brace) - || ahead.peek(token::Paren)) - } || { - let ahead = input.fork(); - ahead.parse::>()?.is_some() && ahead.peek(Token![::]) - } || lookahead.peek(Token![::]) + if lookahead.peek(Ident) + && (input.peek2(Token![::]) + || input.peek2(Token![!]) + || input.peek2(token::Brace) + || input.peek2(token::Paren) + || input.peek2(Token![..])) + || input.peek(Token![self]) && input.peek2(Token![::]) + || lookahead.peek(Token![::]) || lookahead.peek(Token![<]) || input.peek(Token![Self]) || input.peek(Token![super]) - || input.peek(Token![extern]) || input.peek(Token![crate]) { pat_path_or_struct(input) @@ -174,7 +171,7 @@ mod parsing { } else if lookahead.peek(Token![&]) { input.call(pat_reference).map(Pat::Reference) } else if lookahead.peek(token::Paren) { - input.call(pat_tuple).map(Pat::Tuple) + input.call(pat_paren_or_tuple) } else { Err(lookahead.error()) } @@ -189,7 +186,7 @@ mod parsing { } else if input.peek(token::Paren) { pat_tuple_struct(input, path).map(Pat::TupleStruct) } else { - Ok(Pat::Path(PatPath { attrs: Vec::new(), path })) + Ok(Pat::Path(ExprPath { attrs: Vec::new(), qself: None, path })) } } @@ -207,7 +204,21 @@ mod parsing { } fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result { - Ok(PatTupleStruct { attrs: Vec::new(), path, pat: input.call(pat_tuple)? }) + let content; + let paren_token = parenthesized!(content in input); + + let mut elems = Punctuated::new(); + while !content.is_empty() { + let value = Pat::parse_single(&content)?; + elems.push_value(value); + if content.is_empty() { + break; + } + let punct = content.parse()?; + elems.push_punct(punct); + } + + Ok(PatTupleStruct { attrs: Vec::new(), path, paren_token, elems }) } fn pat_struct(input: ParseStream<'_>, path: Path) -> Result { @@ -215,8 +226,15 @@ mod parsing { let brace_token = braced!(content in input); let mut fields = Punctuated::new(); - while !content.is_empty() && !content.peek(Token![..]) { - let value = content.call(field_pat)?; + let mut rest = None; + while !content.is_empty() { + let attrs = content.call(Attribute::parse_outer)?; + if content.peek(Token![..]) { + rest = Some(PatRest { attrs, dot2_token: content.parse()? }); + break; + } + let mut value = content.call(field_pat)?; + value.attrs = attrs; fields.push_value(value); if content.is_empty() { break; @@ -225,30 +243,28 @@ mod parsing { fields.push_punct(punct); } - let dot2_token = if fields.empty_or_trailing() && content.peek(Token![..]) { - Some(content.parse()?) - } else { - None - }; - - Ok(PatStruct { attrs: Vec::new(), path, brace_token, fields, dot2_token }) + Ok(PatStruct { attrs: Vec::new(), path, brace_token, fields, rest }) } fn field_pat(input: ParseStream<'_>) -> Result { - let attrs = input.call(Attribute::parse_outer)?; let boxed: Option = input.parse()?; let by_ref: Option = input.parse()?; let mutability: Option = input.parse()?; - let member: Member = input.parse()?; + + let member = if boxed.is_some() || by_ref.is_some() || mutability.is_some() { + input.parse().map(Member::Named) + } else { + input.parse() + }?; if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) || is_unnamed(&member) { return Ok(FieldPat { - attrs, + attrs: Vec::new(), member, - colon_token: input.parse()?, - pat: input.parse()?, + colon_token: Some(input.parse()?), + pat: Box::new(Pat::parse_single(input)?), }); } @@ -260,25 +276,31 @@ mod parsing { let pat = Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone() }); - Ok(FieldPat { attrs, member: Member::Named(ident), colon_token: None, pat: Box::new(pat) }) + Ok(FieldPat { + attrs: Vec::new(), + member: Member::Named(ident), + colon_token: None, + pat: Box::new(pat), + }) } - fn pat_tuple(input: ParseStream<'_>) -> Result { + fn pat_paren_or_tuple(input: ParseStream<'_>) -> Result { let content; let paren_token = parenthesized!(content in input); let mut elems = Punctuated::new(); while !content.is_empty() { - let value: Pat = content.parse()?; - elems.push_value(value); + let value = Pat::parse_single(&content)?; if content.is_empty() { + elems.push_value(value); break; } + elems.push_value(value); let punct = content.parse()?; elems.push_punct(punct); } - Ok(PatTuple { attrs: Vec::new(), paren_token, elems }) + Ok(Pat::Tuple(PatTuple { attrs: Vec::new(), paren_token, elems })) } fn pat_reference(input: ParseStream<'_>) -> Result { @@ -286,7 +308,7 @@ mod parsing { attrs: Vec::new(), and_token: input.parse()?, mutability: input.parse()?, - pat: input.parse()?, + pat: Box::new(Pat::parse_single(input)?), }) } @@ -304,42 +326,66 @@ mod printing { use syn::Token; use super::{ - FieldPat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType, + FieldPat, PatIdent, PatReference, PatRest, PatStruct, PatTuple, PatTupleStruct, PatType, PatWild, }; - impl ToTokens for PatWild { + impl ToTokens for PatIdent { fn to_tokens(&self, tokens: &mut TokenStream) { - self.underscore_token.to_tokens(tokens); + tokens.append_all(&self.attrs); + self.by_ref.to_tokens(tokens); + self.mutability.to_tokens(tokens); + self.ident.to_tokens(tokens); } } - impl ToTokens for PatIdent { + impl ToTokens for PatReference { fn to_tokens(&self, tokens: &mut TokenStream) { - self.by_ref.to_tokens(tokens); + tokens.append_all(&self.attrs); + self.and_token.to_tokens(tokens); self.mutability.to_tokens(tokens); - self.ident.to_tokens(tokens); + self.pat.to_tokens(tokens); + } + } + + impl ToTokens for PatRest { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.dot2_token.to_tokens(tokens); } } impl ToTokens for PatStruct { fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); self.path.to_tokens(tokens); self.brace_token.surround(tokens, |tokens| { self.fields.to_tokens(tokens); - // NOTE: We need a comma before the dot2 token if it is present. - if !self.fields.empty_or_trailing() && self.dot2_token.is_some() { + // Note: We need a comma before the dot2 token if it is present. + if !self.fields.empty_or_trailing() && self.rest.is_some() { ::default().to_tokens(tokens); } - self.dot2_token.to_tokens(tokens); + self.rest.to_tokens(tokens); + }); + } + } + + impl ToTokens for PatTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.paren_token.surround(tokens, |tokens| { + self.elems.to_tokens(tokens); }); } } impl ToTokens for PatTupleStruct { fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); self.path.to_tokens(tokens); - self.pat.to_tokens(tokens); + self.paren_token.surround(tokens, |tokens| { + self.elems.to_tokens(tokens); + }); } } @@ -352,30 +398,16 @@ mod printing { } } - impl ToTokens for PatPath { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.path.to_tokens(tokens) - } - } - - impl ToTokens for PatTuple { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.paren_token.surround(tokens, |tokens| { - self.elems.to_tokens(tokens); - }); - } - } - - impl ToTokens for PatReference { + impl ToTokens for PatWild { fn to_tokens(&self, tokens: &mut TokenStream) { - self.and_token.to_tokens(tokens); - self.mutability.to_tokens(tokens); - self.pat.to_tokens(tokens); + tokens.append_all(&self.attrs); + self.underscore_token.to_tokens(tokens); } } impl ToTokens for FieldPat { fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); if let Some(colon_token) = &self.colon_token { self.member.to_tokens(tokens); colon_token.to_tokens(tokens); diff --git a/src/path.rs b/src/path.rs index c58a0c1..a643b74 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,3 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Based on https://github.com/dtolnay/syn/blob/2.0.37/src/path.rs. + use syn::{ ext::IdentExt, parse::{ParseStream, Result}, -- cgit v1.2.3