aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2020-06-05 15:50:52 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-06-05 15:50:52 +0000
commit95f10d9c5e7117796b2453021d9514b21fd8fd3b (patch)
tree9bb19e66be1add268cef0c842ef7f922ae6755e6
parentf7fe8c6cb15d05b4bfc922b59416024760c42146 (diff)
parent104e53cd6fef4654c028013dfe0fb29a489247f8 (diff)
downloadsyn-mid-95f10d9c5e7117796b2453021d9514b21fd8fd3b.tar.gz
Import syn-mid-0.5.0 am: 8c1c2809f7 am: d3c67dff88 am: 104e53cd6f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/syn-mid/+/1321939 Change-Id: I4f721a1a533683496efe9b7899e5bfda405e1af2
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--.editorconfig22
-rw-r--r--.gitattributes4
-rw-r--r--.github/CODEOWNERS1
-rw-r--r--.github/bors.toml10
-rw-r--r--.github/workflows/ci.yml95
-rw-r--r--.gitignore7
-rw-r--r--.rustfmt.toml19
-rw-r--r--CHANGELOG.md52
-rw-r--r--Cargo.toml38
-rw-r--r--Cargo.toml.orig26
-rw-r--r--LICENSE-APACHE202
-rw-r--r--LICENSE-MIT23
-rw-r--r--README.md69
-rw-r--r--ci/install-component.sh29
-rw-r--r--ci/install-rust.sh20
-rw-r--r--src/arg.rs99
-rw-r--r--src/lib.rs238
-rw-r--r--src/macros.rs107
-rw-r--r--src/pat.rs389
-rw-r--r--src/path.rs44
21 files changed, 1499 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..69b0ee6
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+ "git": {
+ "sha1": "4cc7697b87772a2eab48efd9dec4e293276dbc20"
+ }
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..2d145b1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,22 @@
+# EditorConfig configuration
+# https://editorconfig.org
+
+# Top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file, utf-8 charset
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+# Match rust/toml, set 4 space indentation
+[*.{rs,toml}]
+indent_style = space
+indent_size = 4
+
+# Match json/yaml/markdown, set 2 space indentation
+[*.{json,yml,md}]
+indent_style = space
+indent_size = 2
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..45bca84
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
+
+* text=auto eol=lf
+*.rs rust
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..2fdc28f
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @taiki-e
diff --git a/.github/bors.toml b/.github/bors.toml
new file mode 100644
index 0000000..b44dd8c
--- /dev/null
+++ b/.github/bors.toml
@@ -0,0 +1,10 @@
+status = [
+ "test (1.31.0)",
+ "test (stable)",
+ "test (beta)",
+ "test (nightly)",
+ "style (clippy)",
+ "style (rustfmt)",
+ "style (rustdoc)",
+]
+delete_merged_branches = true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..c20c9dd
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,95 @@
+name: ci
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+ - staging
+ - trying
+ schedule:
+ - cron: '00 01 * * *'
+
+env:
+ RUSTFLAGS: -Dwarnings
+
+jobs:
+ test:
+ name: test
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ rust:
+ # This is the minimum supported Rust version of this crate.
+ # When updating this, the reminder to update the minimum supported
+ # Rust version in README.md.
+ #
+ # Tests are not run as tests may require newer versions of Rust.
+ - 1.31.0
+ - stable
+ - beta
+ - nightly
+ steps:
+ - uses: actions/checkout@master
+ - name: Install Rust
+ shell: bash
+ run: |
+ . ./ci/install-rust.sh ${{ matrix.rust }}
+ - name: Install cargo-hack
+ if: matrix.rust != '1.31.0'
+ run: |
+ cargo install cargo-hack
+ - name: cargo check
+ if: matrix.rust == '1.31.0'
+ run: |
+ cargo check --all-features
+ - name: cargo test
+ if: matrix.rust != '1.31.0'
+ run: |
+ cargo test --all-features
+ - name: cargo hack check --each-feature
+ if: matrix.rust != '1.31.0'
+ run: |
+ cargo hack check --all --each-feature --no-dev-deps
+ # Refs: https://github.com/rust-lang/cargo/issues/5657
+ - name: cargo check -Zminimal-versions
+ if: matrix.rust == 'nightly'
+ run: |
+ cargo update -Zminimal-versions
+ cargo hack check --all --all-features --no-dev-deps --ignore-private
+
+ style:
+ name: style
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ component:
+ - clippy
+ - rustfmt
+ - rustdoc
+ steps:
+ - uses: actions/checkout@master
+ - name: Install Rust
+ shell: bash
+ run: |
+ . ./ci/install-rust.sh
+ - name: Install component
+ if: matrix.component != 'rustdoc'
+ shell: bash
+ run: |
+ . ./ci/install-component.sh ${{ matrix.component }}
+ - name: cargo clippy
+ if: matrix.component == 'clippy'
+ run: |
+ cargo clippy --all --all-features
+ - name: cargo fmt -- --check
+ if: matrix.component == 'rustfmt'
+ run: |
+ cargo fmt --all -- --check
+ - name: cargo doc
+ if: matrix.component == 'rustdoc'
+ env:
+ RUSTDOCFLAGS: -Dwarnings
+ run: |
+ cargo doc --no-deps --all --all-features
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6a57018
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+target
+**/*.rs.bk
+Cargo.lock
+
+# For platform and editor specific settings, it is recommended to add to
+# a global .gitignore file.
+# Refs: https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..dc49733
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,19 @@
+# Rustfmt configuration
+# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md
+
+# This is required for bug-fixes, which technically can't be made to the stable
+# first version.
+version = "Two" # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3383
+
+# Refs: https://internals.rust-lang.org/t/running-rustfmt-on-rust-lang-rust-and-other-rust-lang-repositories/8732/72
+use_small_heuristics = "Max"
+
+# Apply rustfmt to more places.
+merge_imports = true # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3362
+format_code_in_doc_comments = true # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3348
+
+# Set the default settings again to always apply the proper formatting without
+# being affected by the editor settings.
+# Refs: https://github.com/rust-lang/rls/issues/501#issuecomment-333717736
+edition = "2018"
+tab_spaces = 4
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f404310
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,52 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+This project adheres to [Semantic Versioning](https://semver.org).
+
+## [Unreleased]
+
+## [0.5.0] - 2019-12-09
+
+* [Added `Signature` type.][13]
+
+[13]: https://github.com/taiki-e/syn-mid/pull/13
+
+## [0.4.0] - 2019-08-15
+
+* Updated all data structures based on `syn` 1.0.
+
+* Updated `proc-macro2`, `syn`, and `quote` to 1.0.
+
+* Bumped the minimum required version from Rust 1.30 to Rust 1.31.
+
+## [0.3.0] - 2019-02-18
+
+* Removed support for unneeded syntax.
+
+* Removed unneeded types and fields.
+
+* Implemented `Parse` for `Block`.
+
+* Changed `clone-impls` feature to "disabled by default".
+
+* Removed `extra-traits` feature.
+
+* Bumped the minimum required version from Rust 1.15 to Rust 1.30.
+
+## [0.2.0] - 2019-02-15
+
+* Reduced features.
+
+* Fixed bugs.
+
+## [0.1.0] - 2019-02-14 - YANKED
+
+Initial release
+
+[Unreleased]: https://github.com/taiki-e/syn-mid/compare/v0.5.0...HEAD
+[0.5.0]: https://github.com/taiki-e/syn-mid/compare/v0.4.0...v0.5.0
+[0.4.0]: https://github.com/taiki-e/syn-mid/compare/v0.3.0...v0.4.0
+[0.3.0]: https://github.com/taiki-e/syn-mid/compare/v0.2.0...v0.3.0
+[0.2.0]: https://github.com/taiki-e/syn-mid/compare/v0.1.0...v0.2.0
+[0.1.0]: https://github.com/taiki-e/syn-mid/releases/tag/v0.1.0
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..7191c3e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,38 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# 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
+#
+# 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)
+
+[package]
+edition = "2018"
+name = "syn-mid"
+version = "0.5.0"
+authors = ["Taiki Endo <te316e89@gmail.com>"]
+description = "Providing the features between \"full\" and \"derive\" of syn.\n"
+homepage = "https://github.com/taiki-e/syn-mid"
+documentation = "https://docs.rs/syn-mid"
+readme = "README.md"
+keywords = ["syn", "macros"]
+categories = ["development-tools::procedural-macro-helpers"]
+license = "Apache-2.0 OR MIT"
+repository = "https://github.com/taiki-e/syn-mid"
+[dependencies.proc-macro2]
+version = "1.0"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "1.0.5"
+features = ["parsing", "printing", "derive"]
+default-features = false
+
+[features]
+clone-impls = ["syn/clone-impls"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..55cd2a6
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,26 @@
+[package]
+name = "syn-mid"
+version = "0.5.0"
+authors = ["Taiki Endo <te316e89@gmail.com>"]
+edition = "2018"
+license = "Apache-2.0 OR MIT"
+repository = "https://github.com/taiki-e/syn-mid"
+homepage = "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"
+description = """
+Providing the features between \"full\" and \"derive\" of syn.
+"""
+
+[workspace]
+members = ["examples/const_fn", "examples/const_fn_test"]
+
+[features]
+clone-impls = ["syn/clone-impls"]
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { version = "1.0.5", default-features = false, features = ["parsing", "printing", "derive"] }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..68c0dbd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,69 @@
+# syn-mid
+
+[![crates-badge]][crates-url]
+[![docs-badge]][docs-url]
+[![license-badge]][license]
+[![rustc-badge]][rustc-url]
+
+[crates-badge]: https://img.shields.io/crates/v/syn-mid.svg
+[crates-url]: https://crates.io/crates/syn-mid
+[docs-badge]: https://docs.rs/syn-mid/badge.svg
+[docs-url]: https://docs.rs/syn-mid
+[license-badge]: https://img.shields.io/crates/l/syn-mid.svg
+[license]: #license
+[rustc-badge]: https://img.shields.io/badge/rustc-1.31+-lightgray.svg
+[rustc-url]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
+
+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.
+
+[syn]: https://github.com/dtolnay/syn
+
+## Usage
+
+Add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+syn-mid = "0.5"
+```
+
+The current syn-mid requires Rust 1.31 or later.
+
+[**Examples**](examples)
+
+[**Documentation**](https://docs.rs/syn-mid/)
+
+## Optional features
+
+* **`clone-impls`** — Clone impls for all syntax tree types.
+
+## License
+
+Licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
+* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
diff --git a/ci/install-component.sh b/ci/install-component.sh
new file mode 100644
index 0000000..943755c
--- /dev/null
+++ b/ci/install-component.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -euo pipefail
+
+component="${1}"
+
+if ! rustup component add "${component}" 2>/dev/null; then
+ # If the component is unavailable on the latest nightly,
+ # use the latest toolchain with the component available.
+ # Refs: https://github.com/rust-lang/rustup-components-history#the-web-part
+ target=$(curl -sSf "https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/${component}")
+ echo "'${component}' is unavailable on the default toolchain, use the toolchain 'nightly-${target}' instead"
+
+ rustup update "nightly-${target}" --no-self-update
+ rustup default "nightly-${target}"
+
+ echo "Query rust and cargo versions:"
+ rustup -V
+ rustc -V
+ cargo -V
+
+ rustup component add "${component}"
+fi
+
+echo "Query component versions:"
+case "${component}" in
+ clippy | miri) cargo "${component}" -V ;;
+ rustfmt) "${component}" -V ;;
+esac
diff --git a/ci/install-rust.sh b/ci/install-rust.sh
new file mode 100644
index 0000000..3e0b27a
--- /dev/null
+++ b/ci/install-rust.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -euo pipefail
+
+toolchain="${1:-nightly}"
+
+if rustup -V 2>/dev/null; then
+ rustup set profile minimal
+ rustup update "${toolchain}" --no-self-update
+ rustup default "${toolchain}"
+else
+ curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${toolchain}"
+ export PATH=${PATH}:${HOME}/.cargo/bin
+ echo "##[add-path]${HOME}/.cargo/bin"
+fi
+
+echo "Query rust and cargo versions:"
+rustup -V
+rustc -V
+cargo -V
diff --git a/src/arg.rs b/src/arg.rs
new file mode 100644
index 0000000..593a1ac
--- /dev/null
+++ b/src/arg.rs
@@ -0,0 +1,99 @@
+use syn::{Attribute, Lifetime, Token};
+
+use super::PatType;
+
+ast_enum_of_structs! {
+ /// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
+ pub enum FnArg {
+ /// The `self` argument of an associated method, whether taken by value
+ /// or by reference.
+ Receiver(Receiver),
+
+ /// A function argument accepted by pattern and type.
+ Typed(PatType),
+ }
+}
+
+ast_struct! {
+ /// The `self` argument of an associated method, whether taken by value
+ /// or by reference.
+ pub struct Receiver {
+ pub attrs: Vec<Attribute>,
+ pub reference: Option<(Token![&], Option<Lifetime>)>,
+ pub mutability: Option<Token![mut]>,
+ pub self_token: Token![self],
+ }
+}
+
+mod parsing {
+ use syn::{
+ parse::{discouraged::Speculative, Parse, ParseStream, Result},
+ Attribute, Token,
+ };
+
+ use super::{FnArg, PatType, Receiver};
+
+ impl Parse for FnArg {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
+
+ let ahead = input.fork();
+ if let Ok(mut receiver) = ahead.parse::<Receiver>() {
+ if !ahead.peek(Token![:]) {
+ input.advance_to(&ahead);
+ receiver.attrs = attrs;
+ return Ok(FnArg::Receiver(receiver));
+ }
+ }
+
+ let mut typed = input.call(fn_arg_typed)?;
+ typed.attrs = attrs;
+ Ok(FnArg::Typed(typed))
+ }
+ }
+
+ impl Parse for Receiver {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ Ok(Self {
+ attrs: Vec::new(),
+ reference: {
+ if input.peek(Token![&]) {
+ Some((input.parse()?, input.parse()?))
+ } else {
+ None
+ }
+ },
+ mutability: input.parse()?,
+ self_token: input.parse()?,
+ })
+ }
+ }
+
+ fn fn_arg_typed(input: ParseStream<'_>) -> Result<PatType> {
+ Ok(PatType {
+ attrs: Vec::new(),
+ pat: input.parse()?,
+ colon_token: input.parse()?,
+ ty: Box::new(input.parse()?),
+ })
+ }
+}
+
+mod printing {
+ use proc_macro2::TokenStream;
+ use quote::{ToTokens, TokenStreamExt};
+
+ use super::Receiver;
+
+ impl ToTokens for Receiver {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append_all(&self.attrs);
+ if let Some((ampersand, lifetime)) = &self.reference {
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ }
+ self.mutability.to_tokens(tokens);
+ self.self_token.to_tokens(tokens);
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..3b21506
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,238 @@
+//! 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
+//!
+//! syn-mid in the default features aims to provide the features between "full"
+//! and "derive" of [syn].
+//!
+//! * **`clone-impls`** — Clone impls for all syntax tree types.
+//!
+//! [`syn_mid::ItemFn`]: struct.ItemFn.html
+//! [`syn_mid::Block`]: struct.Block.html
+//! [syn]: https://github.com/dtolnay/syn
+
+#![doc(html_root_url = "https://docs.rs/syn-mid/0.5.0")]
+#![doc(test(
+ no_crate_inject,
+ attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
+))]
+#![forbid(unsafe_code)]
+#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
+#![warn(clippy::all)]
+#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]
+
+// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.
+
+#[macro_use]
+mod macros;
+
+mod arg;
+mod pat;
+mod path;
+
+pub use self::{arg::*, pat::*};
+
+use proc_macro2::TokenStream;
+use syn::{
+ punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, Variadic,
+ Visibility,
+};
+
+ast_struct! {
+ /// A braced block containing Rust statements.
+ pub struct Block {
+ pub brace_token: token::Brace,
+ /// Statements in a block
+ pub stmts: TokenStream,
+ }
+}
+
+ast_struct! {
+ /// A free-standing function: `fn process(n: usize) -> Result<()> { ...
+ /// }`.
+ pub struct ItemFn {
+ pub attrs: Vec<Attribute>,
+ pub vis: Visibility,
+ pub sig: Signature,
+ pub block: Box<Block>,
+ }
+}
+
+ast_struct! {
+ /// A function signature in a trait or implementation: `unsafe fn
+ /// initialize(&self)`.
+ pub struct Signature {
+ pub constness: Option<Token![const]>,
+ pub asyncness: Option<Token![async]>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub abi: Option<Abi>,
+ pub fn_token: Token![fn],
+ pub ident: Ident,
+ pub generics: Generics,
+ pub paren_token: token::Paren,
+ pub inputs: Punctuated<FnArg, Token![,]>,
+ pub variadic: Option<Variadic>,
+ pub output: ReturnType,
+ }
+}
+
+mod parsing {
+ use syn::{
+ braced, parenthesized,
+ parse::{Parse, ParseStream, Result},
+ parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility,
+ WhereClause,
+ };
+
+ use super::{Block, FnArg, ItemFn, PatType, Signature};
+
+ impl Parse for Block {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let content;
+ Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
+ }
+ }
+
+ impl Parse for ItemFn {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
+ let vis: Visibility = input.parse()?;
+ let constness: Option<Token![const]> = input.parse()?;
+ let asyncness: Option<Token![async]> = input.parse()?;
+ let unsafety: Option<Token![unsafe]> = input.parse()?;
+ let abi: Option<Abi> = input.parse()?;
+ let fn_token: Token![fn] = input.parse()?;
+ let ident: Ident = input.parse()?;
+ let generics: Generics = input.parse()?;
+
+ let content;
+ let paren_token = parenthesized!(content in input);
+ let inputs = content.parse_terminated(FnArg::parse)?;
+ let variadic = inputs.last().as_ref().and_then(get_variadic);
+
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ fn get_variadic(input: &&FnArg) -> Option<Variadic> {
+ 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 });
+ }
+ }
+ }
+ None
+ }
+
+ let output: ReturnType = input.parse()?;
+ let where_clause: Option<WhereClause> = input.parse()?;
+
+ let block = input.parse()?;
+
+ Ok(Self {
+ attrs,
+ vis,
+ sig: Signature {
+ constness,
+ asyncness,
+ unsafety,
+ abi,
+ fn_token,
+ ident,
+ paren_token,
+ inputs,
+ output,
+ variadic,
+ generics: Generics { where_clause, ..generics },
+ },
+ block: Box::new(block),
+ })
+ }
+ }
+}
+
+mod printing {
+ use proc_macro2::TokenStream;
+ use quote::{ToTokens, TokenStreamExt};
+ use syn::{punctuated::Punctuated, Token, Type};
+
+ use super::{Block, FnArg, ItemFn, Signature};
+
+ impl ToTokens for Block {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.brace_token.surround(tokens, |tokens| {
+ tokens.append_all(self.stmts.clone());
+ });
+ }
+ }
+
+ fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> 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.as_ref() {
+ 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);
+ self.asyncness.to_tokens(tokens);
+ self.unsafety.to_tokens(tokens);
+ self.abi.to_tokens(tokens);
+ self.fn_token.to_tokens(tokens);
+ self.ident.to_tokens(tokens);
+ 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 !self.inputs.empty_or_trailing() {
+ <Token![,]>::default().to_tokens(tokens);
+ }
+ self.variadic.to_tokens(tokens);
+ }
+ });
+ self.output.to_tokens(tokens);
+ self.generics.where_clause.to_tokens(tokens);
+ }
+ }
+
+ 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());
+ });
+ }
+ }
+}
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..87be7b4
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,107 @@
+macro_rules! ast_struct {
+ (
+ [$($attrs_pub:tt)*]
+ struct $name:ident $($rest:tt)*
+ ) => {
+ #[cfg_attr(feature = "clone-impls", derive(Clone))]
+ $($attrs_pub)* struct $name $($rest)*
+ };
+
+ ($($t:tt)*) => {
+ strip_attrs_pub!(ast_struct!($($t)*));
+ };
+}
+
+macro_rules! ast_enum {
+ (
+ [$($attrs_pub:tt)*]
+ enum $name:ident $($rest:tt)*
+ ) => (
+ #[cfg_attr(feature = "clone-impls", derive(Clone))]
+ $($attrs_pub)* enum $name $($rest)*
+ );
+
+ ($($t:tt)*) => {
+ strip_attrs_pub!(ast_enum!($($t)*));
+ };
+}
+
+macro_rules! ast_enum_of_structs {
+ (
+ $(#[$enum_attr:meta])*
+ $pub:ident $enum:ident $name:ident $body:tt
+ ) => {
+ ast_enum!($(#[$enum_attr])* $pub $enum $name $body);
+ ast_enum_of_structs_impl!($pub $enum $name $body);
+ };
+}
+
+macro_rules! ast_enum_of_structs_impl {
+ (
+ $pub:ident $enum:ident $name:ident {
+ $(
+ $(#[$variant_attr:meta])*
+ $variant:ident $( ($member:ident) )*,
+ )*
+ }
+ ) => {
+ check_keyword_matches!(pub $pub);
+ check_keyword_matches!(enum $enum);
+
+ $(
+ $(
+ impl From<$member> for $name {
+ fn from(e: $member) -> $name {
+ $name::$variant(e)
+ }
+ }
+ )*
+ )*
+
+ generate_to_tokens! {
+ ()
+ tokens
+ $name { $($variant $($member)*,)* }
+ }
+ };
+}
+
+macro_rules! generate_to_tokens {
+ (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident, $($next:tt)*}) => {
+ generate_to_tokens!(
+ ($($arms)* $name::$variant => {})
+ $tokens $name { $($next)* }
+ );
+ };
+
+ (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident $member:ident, $($next:tt)*}) => {
+ generate_to_tokens!(
+ ($($arms)* $name::$variant(_e) => quote::ToTokens::to_tokens(_e, $tokens),)
+ $tokens $name { $($next)* }
+ );
+ };
+
+ (($($arms:tt)*) $tokens:ident $name:ident {}) => {
+ impl quote::ToTokens for $name {
+ fn to_tokens(&self, $tokens: &mut proc_macro2::TokenStream) {
+ match self {
+ $($arms)*
+ }
+ }
+ }
+ };
+}
+
+macro_rules! strip_attrs_pub {
+ ($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => {
+ check_keyword_matches!(pub $pub);
+
+ $mac!([$(#[$m])* $pub] $($t)*);
+ };
+}
+
+macro_rules! check_keyword_matches {
+ (struct struct) => {};
+ (enum enum) => {};
+ (pub pub) => {};
+}
diff --git a/src/pat.rs b/src/pat.rs
new file mode 100644
index 0000000..ff1277f
--- /dev/null
+++ b/src/pat.rs
@@ -0,0 +1,389 @@
+use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type};
+
+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]: enum.Expr.html#syntax-tree-enums
+ pub enum Pat {
+ /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`.
+ Ident(PatIdent),
+
+ /// A path pattern like `Color::Red`.
+ Path(PatPath),
+
+ /// A reference pattern: `&mut var`.
+ Reference(PatReference),
+
+ /// A struct or struct variant pattern: `Variant { x, y, .. }`.
+ Struct(PatStruct),
+
+ /// A tuple pattern: `(a, b)`.
+ Tuple(PatTuple),
+
+ /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`.
+ TupleStruct(PatTupleStruct),
+
+ /// A type ascription pattern: `foo: f64`.
+ Type(PatType),
+
+ /// A pattern that matches any value: `_`.
+ Wild(PatWild),
+
+ #[doc(hidden)]
+ __Nonexhaustive,
+ }
+}
+
+ast_struct! {
+ /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`.
+ pub struct PatIdent {
+ pub attrs: Vec<Attribute>,
+ pub by_ref: Option<Token![ref]>,
+ pub mutability: Option<Token![mut]>,
+ pub ident: Ident,
+ }
+}
+
+ast_struct! {
+ /// A path pattern like `Color::Red`.
+ pub struct PatPath {
+ pub attrs: Vec<Attribute>,
+ pub path: Path,
+ }
+}
+
+ast_struct! {
+ /// A reference pattern: `&mut var`.
+ pub struct PatReference {
+ pub attrs: Vec<Attribute>,
+ pub and_token: Token![&],
+ pub mutability: Option<Token![mut]>,
+ pub pat: Box<Pat>,
+ }
+}
+
+ast_struct! {
+ /// A struct or struct variant pattern: `Variant { x, y, .. }`.
+ pub struct PatStruct {
+ pub attrs: Vec<Attribute>,
+ pub path: Path,
+ pub brace_token: token::Brace,
+ pub fields: Punctuated<FieldPat, Token![,]>,
+ pub dot2_token: Option<Token![..]>,
+ }
+}
+
+ast_struct! {
+ /// A tuple pattern: `(a, b)`.
+ pub struct PatTuple {
+ pub attrs: Vec<Attribute>,
+ pub paren_token: token::Paren,
+ pub elems: Punctuated<Pat, Token![,]>,
+ }
+}
+
+ast_struct! {
+ /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`.
+ pub struct PatTupleStruct {
+ pub attrs: Vec<Attribute>,
+ pub path: Path,
+ pub pat: PatTuple,
+ }
+}
+
+ast_struct! {
+ /// A type ascription pattern: `foo: f64`.
+ pub struct PatType {
+ pub attrs: Vec<Attribute>,
+ pub pat: Box<Pat>,
+ pub colon_token: Token![:],
+ pub ty: Box<Type>,
+ }
+}
+
+ast_struct! {
+ /// A pattern that matches any value: `_`.
+ pub struct PatWild {
+ pub attrs: Vec<Attribute>,
+ pub underscore_token: Token![_],
+ }
+}
+
+ast_struct! {
+ /// A single field in a struct pattern.
+ ///
+ /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated
+ /// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token.
+ pub struct FieldPat {
+ pub attrs: Vec<Attribute>,
+ pub member: Member,
+ pub colon_token: Option<Token![:]>,
+ pub pat: Box<Pat>,
+ }
+}
+
+mod parsing {
+ use syn::{
+ braced,
+ ext::IdentExt,
+ parenthesized,
+ parse::{Parse, ParseStream, Result},
+ punctuated::Punctuated,
+ token, Ident, Member, Path, Token,
+ };
+
+ use crate::path;
+
+ use super::{
+ FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct,
+ PatWild,
+ };
+
+ impl Parse for Pat {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Ident)
+ && ({
+ input.peek2(Token![::])
+ || input.peek2(token::Brace)
+ || input.peek2(token::Paren)
+ })
+ || 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)
+ } else if lookahead.peek(Token![_]) {
+ input.call(pat_wild).map(Pat::Wild)
+ } else if lookahead.peek(Token![ref])
+ || lookahead.peek(Token![mut])
+ || input.peek(Token![self])
+ || input.peek(Ident)
+ {
+ input.call(pat_ident).map(Pat::Ident)
+ } 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)
+ } else {
+ Err(lookahead.error())
+ }
+ }
+ }
+
+ fn pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat> {
+ let path = path::parse_path(input)?;
+
+ if input.peek(token::Brace) {
+ pat_struct(input, path).map(Pat::Struct)
+ } else if input.peek(token::Paren) {
+ pat_tuple_struct(input, path).map(Pat::TupleStruct)
+ } else {
+ Ok(Pat::Path(PatPath { attrs: Vec::new(), path }))
+ }
+ }
+
+ fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> {
+ Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()? })
+ }
+
+ fn pat_ident(input: ParseStream<'_>) -> Result<PatIdent> {
+ Ok(PatIdent {
+ attrs: Vec::new(),
+ by_ref: input.parse()?,
+ mutability: input.parse()?,
+ ident: input.call(Ident::parse_any)?,
+ })
+ }
+
+ fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct> {
+ Ok(PatTupleStruct { attrs: Vec::new(), path, pat: input.call(pat_tuple)? })
+ }
+
+ fn pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct> {
+ let content;
+ 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)?;
+ fields.push_value(value);
+ if !content.peek(Token![,]) {
+ break;
+ }
+ let punct: Token![,] = content.parse()?;
+ 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 })
+ }
+
+ fn field_pat(input: ParseStream<'_>) -> Result<FieldPat> {
+ let boxed: Option<Token![box]> = input.parse()?;
+ let by_ref: Option<Token![ref]> = input.parse()?;
+ let mutability: Option<Token![mut]> = input.parse()?;
+ let member: Member = input.parse()?;
+
+ if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:])
+ || is_unnamed(&member)
+ {
+ return Ok(FieldPat {
+ attrs: Vec::new(),
+ member,
+ colon_token: input.parse()?,
+ pat: input.parse()?,
+ });
+ }
+
+ let ident = match member {
+ Member::Named(ident) => ident,
+ Member::Unnamed(_) => unreachable!(),
+ };
+
+ let pat =
+ Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone() });
+
+ Ok(FieldPat {
+ member: Member::Named(ident),
+ pat: Box::new(pat),
+ attrs: Vec::new(),
+ colon_token: None,
+ })
+ }
+
+ fn pat_tuple(input: ParseStream<'_>) -> Result<PatTuple> {
+ 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);
+ if content.is_empty() {
+ break;
+ }
+ let punct = content.parse()?;
+ elems.push_punct(punct);
+ }
+
+ Ok(PatTuple { attrs: Vec::new(), paren_token, elems })
+ }
+
+ fn pat_reference(input: ParseStream<'_>) -> Result<PatReference> {
+ Ok(PatReference {
+ attrs: Vec::new(),
+ and_token: input.parse()?,
+ mutability: input.parse()?,
+ pat: input.parse()?,
+ })
+ }
+
+ fn is_unnamed(member: &Member) -> bool {
+ match member {
+ Member::Named(_) => false,
+ Member::Unnamed(_) => true,
+ }
+ }
+}
+
+mod printing {
+ use proc_macro2::TokenStream;
+ use quote::{ToTokens, TokenStreamExt};
+ use syn::Token;
+
+ use super::{
+ FieldPat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType,
+ PatWild,
+ };
+
+ impl ToTokens for PatWild {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.underscore_token.to_tokens(tokens);
+ }
+ }
+
+ impl ToTokens for PatIdent {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.by_ref.to_tokens(tokens);
+ self.mutability.to_tokens(tokens);
+ self.ident.to_tokens(tokens);
+ }
+ }
+
+ impl ToTokens for PatStruct {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ 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() {
+ <Token![,]>::default().to_tokens(tokens);
+ }
+ self.dot2_token.to_tokens(tokens);
+ });
+ }
+ }
+
+ impl ToTokens for PatTupleStruct {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.path.to_tokens(tokens);
+ self.pat.to_tokens(tokens);
+ }
+ }
+
+ impl ToTokens for PatType {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append_all(&self.attrs);
+ self.pat.to_tokens(tokens);
+ self.colon_token.to_tokens(tokens);
+ self.ty.to_tokens(tokens);
+ }
+ }
+
+ 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 {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.and_token.to_tokens(tokens);
+ self.mutability.to_tokens(tokens);
+ self.pat.to_tokens(tokens);
+ }
+ }
+
+ impl ToTokens for FieldPat {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if let Some(colon_token) = &self.colon_token {
+ self.member.to_tokens(tokens);
+ colon_token.to_tokens(tokens);
+ }
+ self.pat.to_tokens(tokens);
+ }
+ }
+}
diff --git a/src/path.rs b/src/path.rs
new file mode 100644
index 0000000..408cda7
--- /dev/null
+++ b/src/path.rs
@@ -0,0 +1,44 @@
+use syn::{
+ ext::IdentExt,
+ parse::{ParseStream, Result},
+ punctuated::Punctuated,
+ Ident, Path, PathArguments, PathSegment, Token,
+};
+
+fn parse_path_segment(input: ParseStream<'_>) -> Result<PathSegment> {
+ if input.peek(Token![super])
+ || input.peek(Token![self])
+ || input.peek(Token![crate])
+ || input.peek(Token![extern])
+ {
+ let ident = input.call(Ident::parse_any)?;
+ return Ok(PathSegment::from(ident));
+ }
+
+ let ident =
+ if input.peek(Token![Self]) { input.call(Ident::parse_any)? } else { input.parse()? };
+
+ if input.peek(Token![::]) && input.peek3(Token![<]) {
+ Ok(PathSegment { ident, arguments: PathArguments::AngleBracketed(input.parse()?) })
+ } else {
+ Ok(PathSegment::from(ident))
+ }
+}
+
+pub(crate) fn parse_path(input: ParseStream<'_>) -> Result<Path> {
+ Ok(Path {
+ leading_colon: input.parse()?,
+ segments: {
+ let mut segments = Punctuated::new();
+ let value = parse_path_segment(input)?;
+ segments.push_value(value);
+ while input.peek(Token![::]) {
+ let punct: Token![::] = input.parse()?;
+ segments.push_punct(punct);
+ let value = parse_path_segment(input)?;
+ segments.push_value(value);
+ }
+ segments
+ },
+ })
+}