aboutsummaryrefslogtreecommitdiff
path: root/syn-mid
diff options
context:
space:
mode:
Diffstat (limited to 'syn-mid')
-rw-r--r--syn-mid/.editorconfig22
-rw-r--r--syn-mid/.gitignore6
-rw-r--r--syn-mid/.rustfmt.toml4
-rw-r--r--syn-mid/CHANGELOG.md33
-rw-r--r--syn-mid/Cargo.toml23
-rw-r--r--syn-mid/LICENSE-APACHE202
-rw-r--r--syn-mid/LICENSE-MIT23
-rw-r--r--syn-mid/README.md72
-rw-r--r--syn-mid/azure-pipelines.yml49
-rw-r--r--syn-mid/bors.toml1
-rw-r--r--syn-mid/ci/azure-clippy.yml31
-rw-r--r--syn-mid/ci/azure-install-rust.yml33
-rw-r--r--syn-mid/ci/azure-rustdoc.yml13
-rw-r--r--syn-mid/ci/azure-rustfmt.yml18
-rw-r--r--syn-mid/ci/azure-test.yml34
-rw-r--r--syn-mid/examples/const_fn/Cargo.toml16
-rw-r--r--syn-mid/examples/const_fn/lib.rs31
-rw-r--r--syn-mid/examples/const_fn_test/Cargo.toml9
-rw-r--r--syn-mid/examples/const_fn_test/build.rs16
-rw-r--r--syn-mid/examples/const_fn_test/tests/test.rs25
-rw-r--r--syn-mid/src/arg.rs99
-rw-r--r--syn-mid/src/lib.rs190
-rw-r--r--syn-mid/src/macros.rs107
-rw-r--r--syn-mid/src/pat.rs413
-rw-r--r--syn-mid/src/path.rs50
25 files changed, 1520 insertions, 0 deletions
diff --git a/syn-mid/.editorconfig b/syn-mid/.editorconfig
new file mode 100644
index 0000000..920ea40
--- /dev/null
+++ b/syn-mid/.editorconfig
@@ -0,0 +1,22 @@
+# EditorConfig configuration
+# http://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 yaml/markdown, set 2 space indentation
+[*.{yml,md}]
+indent_style = space
+indent_size = 2
diff --git a/syn-mid/.gitignore b/syn-mid/.gitignore
new file mode 100644
index 0000000..2e6c307
--- /dev/null
+++ b/syn-mid/.gitignore
@@ -0,0 +1,6 @@
+.idea/
+.vscode/
+target/
+**/*.rs.bk
+Cargo.lock
+___*
diff --git a/syn-mid/.rustfmt.toml b/syn-mid/.rustfmt.toml
new file mode 100644
index 0000000..a27c0b7
--- /dev/null
+++ b/syn-mid/.rustfmt.toml
@@ -0,0 +1,4 @@
+# Set the default settings again to always apply the proper formatting without being affected by the editor settings.
+# https://github.com/rust-lang/rls/issues/501#issuecomment-333717736
+edition = "2018"
+tab_spaces = 4
diff --git a/syn-mid/CHANGELOG.md b/syn-mid/CHANGELOG.md
new file mode 100644
index 0000000..25c17c1
--- /dev/null
+++ b/syn-mid/CHANGELOG.md
@@ -0,0 +1,33 @@
+# Unreleased
+
+# 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
+
+Initial release
diff --git a/syn-mid/Cargo.toml b/syn-mid/Cargo.toml
new file mode 100644
index 0000000..f92e7a0
--- /dev/null
+++ b/syn-mid/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "syn-mid"
+version = "0.4.0"
+authors = ["Taiki Endo <te316e89@gmail.com>"]
+edition = "2018"
+license = "Apache-2.0/MIT"
+repository = "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"]
+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", default-features = false, features = ["parsing", "printing", "derive"] }
diff --git a/syn-mid/LICENSE-APACHE b/syn-mid/LICENSE-APACHE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/syn-mid/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/syn-mid/LICENSE-MIT b/syn-mid/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/syn-mid/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/syn-mid/README.md b/syn-mid/README.md
new file mode 100644
index 0000000..b9b1c75
--- /dev/null
+++ b/syn-mid/README.md
@@ -0,0 +1,72 @@
+# syn-mid
+
+[![Build Status][azure-badge]][azure-url]
+[![Crates.io][crates-version-badge]][crates-url]
+[![Docs.rs][docs-badge]][docs-url]
+[![License][crates-license-badge]][crates-url]
+[![Minimum supported Rust version][rustc-badge]][rustc-url]
+
+[azure-badge]: https://dev.azure.com/taiki-e/taiki-e/_apis/build/status/taiki-e.syn-mid?branchName=master
+[azure-url]: https://dev.azure.com/taiki-e/taiki-e/_build/latest?definitionId=11&branchName=master
+[crates-version-badge]: https://img.shields.io/crates/v/syn-mid.svg
+[crates-license-badge]: https://img.shields.io/crates/l/syn-mid.svg
+[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/
+[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.4"
+```
+
+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/syn-mid/azure-pipelines.yml b/syn-mid/azure-pipelines.yml
new file mode 100644
index 0000000..6f0c968
--- /dev/null
+++ b/syn-mid/azure-pipelines.yml
@@ -0,0 +1,49 @@
+trigger:
+- master
+- staging
+- trying
+
+variables:
+ RUSTFLAGS: -Dwarnings
+
+jobs:
+# This is the minimum Rust version supported by syn-mid.
+# 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.
+- template: ci/azure-test.yml
+ parameters:
+ name: minrust
+ rust: 1.31.0
+
+- template: ci/azure-test.yml
+ parameters:
+ name: stable
+ rust: stable
+
+- template: ci/azure-test.yml
+ parameters:
+ name: beta
+ rust: beta
+
+- template: ci/azure-test.yml
+ parameters:
+ name: nightly
+ rust: nightly
+ cmd: test
+
+- template: ci/azure-clippy.yml
+ parameters:
+ name: clippy
+ rust: nightly
+
+- template: ci/azure-rustfmt.yml
+ parameters:
+ name: rustfmt
+ rust: stable
+
+- template: ci/azure-rustdoc.yml
+ parameters:
+ name: rustdoc
+ rust: nightly
diff --git a/syn-mid/bors.toml b/syn-mid/bors.toml
new file mode 100644
index 0000000..b477894
--- /dev/null
+++ b/syn-mid/bors.toml
@@ -0,0 +1 @@
+status = ["taiki-e.syn-mid"]
diff --git a/syn-mid/ci/azure-clippy.yml b/syn-mid/ci/azure-clippy.yml
new file mode 100644
index 0000000..22165c6
--- /dev/null
+++ b/syn-mid/ci/azure-clippy.yml
@@ -0,0 +1,31 @@
+jobs:
+- job: ${{ parameters.name }}
+ displayName: Clippy
+ pool:
+ vmImage: ubuntu-16.04
+
+ steps:
+ - template: azure-install-rust.yml
+ parameters:
+ rust: ${{ parameters.rust }}
+
+ - script: |
+ set +e
+ if rustup component add clippy; then
+ set -e
+ else
+ set -e
+ target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy`
+ echo "'clippy' is unavailable on the toolchain '${{ parameters.rust }}', use the toolchain 'nightly-$target' instead"
+ rustup toolchain install nightly-$target
+ rustup default nightly-$target
+ rustup component add clippy
+ rustup toolchain list
+ rustc -Vv
+ cargo -V
+ fi
+ cargo clippy --version
+ displayName: rustup component add clippy
+
+ - script: cargo clippy --all --all-features
+ displayName: cargo clippy --all-features
diff --git a/syn-mid/ci/azure-install-rust.yml b/syn-mid/ci/azure-install-rust.yml
new file mode 100644
index 0000000..6b008c6
--- /dev/null
+++ b/syn-mid/ci/azure-install-rust.yml
@@ -0,0 +1,33 @@
+steps:
+ # Linux and macOS.
+ - script: |
+ set -e
+ curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
+ export PATH=$PATH:$HOME/.cargo/bin
+ rustup toolchain install $RUSTUP_TOOLCHAIN
+ rustup default $RUSTUP_TOOLCHAIN
+ echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
+ env:
+ RUSTUP_TOOLCHAIN: ${{ parameters.rust }}
+ displayName: Install rust (*nix)
+ condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
+
+ # Windows.
+ - script: |
+ curl -sSf -o rustup-init.exe https://win.rustup.rs
+ rustup-init.exe -y --default-toolchain none
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ rustup toolchain install %RUSTUP_TOOLCHAIN%
+ rustup default %RUSTUP_TOOLCHAIN%
+ echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
+ env:
+ RUSTUP_TOOLCHAIN: ${{ parameters.rust }}
+ displayName: Install rust (windows)
+ condition: eq(variables['Agent.OS'], 'Windows_NT')
+
+ # All platforms.
+ - script: |
+ rustup toolchain list
+ rustc -Vv
+ cargo -V
+ displayName: Query rust and cargo versions
diff --git a/syn-mid/ci/azure-rustdoc.yml b/syn-mid/ci/azure-rustdoc.yml
new file mode 100644
index 0000000..99a43ff
--- /dev/null
+++ b/syn-mid/ci/azure-rustdoc.yml
@@ -0,0 +1,13 @@
+jobs:
+- job: ${{ parameters.name }}
+ displayName: Rustdoc
+ pool:
+ vmImage: ubuntu-16.04
+
+ steps:
+ - template: azure-install-rust.yml
+ parameters:
+ rust: ${{ parameters.rust }}
+
+ - script: RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps --all --all-features
+ displayName: cargo doc --all-features
diff --git a/syn-mid/ci/azure-rustfmt.yml b/syn-mid/ci/azure-rustfmt.yml
new file mode 100644
index 0000000..0b20da3
--- /dev/null
+++ b/syn-mid/ci/azure-rustfmt.yml
@@ -0,0 +1,18 @@
+jobs:
+- job: ${{ parameters.name }}
+ displayName: Rustfmt
+ pool:
+ vmImage: ubuntu-16.04
+
+ steps:
+ - template: azure-install-rust.yml
+ parameters:
+ rust: ${{ parameters.rust }}
+
+ - script: |
+ rustup component add rustfmt
+ cargo fmt --version
+ displayName: rustup component add rustfmt
+
+ - script: cargo fmt --all -- --check
+ displayName: cargo fmt -- --check
diff --git a/syn-mid/ci/azure-test.yml b/syn-mid/ci/azure-test.yml
new file mode 100644
index 0000000..32a56ed
--- /dev/null
+++ b/syn-mid/ci/azure-test.yml
@@ -0,0 +1,34 @@
+parameters:
+ cmd: check
+
+jobs:
+- job: ${{ parameters.name }}
+ displayName: ${{ parameters.displayName }} ${{ parameters.rust }}
+ strategy:
+ matrix:
+ Linux:
+ vmImage: ubuntu-16.04
+
+ ${{ if parameters.cross }}:
+ MacOS:
+ vmImage: macOS-10.13
+ Windows:
+ vmImage: vs2017-win2016
+ pool:
+ vmImage: $(vmImage)
+
+ steps:
+ - template: azure-install-rust.yml
+ parameters:
+ rust: ${{ parameters.rust }}
+
+ - script: |
+ cargo ${{ parameters.cmd }}
+ cargo ${{ parameters.cmd }} --all-features
+ displayName: cargo ${{ parameters.cmd }}
+
+ - ${{ if eq(parameters.rust, 'nightly') }}:
+ - script: |
+ cargo update -Zminimal-versions
+ cargo check --all-features
+ displayName: cargo check -Zminimal-versions
diff --git a/syn-mid/examples/const_fn/Cargo.toml b/syn-mid/examples/const_fn/Cargo.toml
new file mode 100644
index 0000000..d823e76
--- /dev/null
+++ b/syn-mid/examples/const_fn/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "const_fn"
+version = "0.0.0"
+authors = ["Taiki Endo <te316e89@gmail.com>"]
+edition = "2018"
+publish = false
+
+[lib]
+proc-macro = true
+path = "lib.rs"
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = "1.0"
+syn-mid = { version = "0.4", path = "../..", features = ["clone-impls"] }
diff --git a/syn-mid/examples/const_fn/lib.rs b/syn-mid/examples/const_fn/lib.rs
new file mode 100644
index 0000000..29255f2
--- /dev/null
+++ b/syn-mid/examples/const_fn/lib.rs
@@ -0,0 +1,31 @@
+#![warn(rust_2018_idioms)]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use syn_mid::ItemFn;
+
+/// An attribute for easy generation of a const function with conditional compilations.
+#[proc_macro_attribute]
+pub fn const_fn(args: TokenStream, function: TokenStream) -> TokenStream {
+ assert!(!args.is_empty(), "requires an argument");
+
+ let mut function = syn::parse_macro_input!(function as ItemFn);
+ let mut const_function = function.clone();
+
+ if function.constness.is_some() {
+ function.constness = None;
+ } else {
+ const_function.constness = Some(Default::default());
+ }
+
+ let args = TokenStream2::from(args);
+ TokenStream::from(quote! {
+ #[cfg(not(#args))]
+ #function
+ #[cfg(#args)]
+ #const_function
+ })
+}
diff --git a/syn-mid/examples/const_fn_test/Cargo.toml b/syn-mid/examples/const_fn_test/Cargo.toml
new file mode 100644
index 0000000..b3e2807
--- /dev/null
+++ b/syn-mid/examples/const_fn_test/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "const_fn_test"
+version = "0.0.0"
+authors = ["Taiki Endo <te316e89@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+const_fn = { path = "../const_fn" }
diff --git a/syn-mid/examples/const_fn_test/build.rs b/syn-mid/examples/const_fn_test/build.rs
new file mode 100644
index 0000000..bebf234
--- /dev/null
+++ b/syn-mid/examples/const_fn_test/build.rs
@@ -0,0 +1,16 @@
+use std::{env, process::Command};
+
+fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+
+ if is_nightly() {
+ println!("cargo:rustc-cfg=nightly");
+ }
+}
+
+fn is_nightly() -> bool {
+ env::var_os("RUSTC")
+ .and_then(|rustc| Command::new(rustc).arg("--version").output().ok())
+ .and_then(|output| String::from_utf8(output.stdout).ok())
+ .map_or(false, |version| version.contains("nightly"))
+}
diff --git a/syn-mid/examples/const_fn_test/tests/test.rs b/syn-mid/examples/const_fn_test/tests/test.rs
new file mode 100644
index 0000000..1b2c742
--- /dev/null
+++ b/syn-mid/examples/const_fn_test/tests/test.rs
@@ -0,0 +1,25 @@
+#![cfg_attr(nightly, feature(const_fn, const_vec_new))]
+#![warn(rust_2018_idioms)]
+#![allow(dead_code)]
+
+use const_fn::const_fn;
+
+#[const_fn(nightly)]
+fn const_vec_new<T>() -> Vec<T> {
+ let vec = Vec::new();
+ vec
+}
+
+#[test]
+fn test_stable() {
+ assert_eq!(const_vec_new::<u8>(), Vec::new());
+}
+
+#[cfg(nightly)]
+const CONST_UNSTABLE: Vec<u8> = const_vec_new();
+
+#[cfg(nightly)]
+#[test]
+fn test_unstable() {
+ assert_eq!(CONST_UNSTABLE, Vec::new());
+}
diff --git a/syn-mid/src/arg.rs b/syn-mid/src/arg.rs
new file mode 100644
index 0000000..593a1ac
--- /dev/null
+++ b/syn-mid/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/syn-mid/src/lib.rs b/syn-mid/src/lib.rs
new file mode 100644
index 0000000..69bdec9
--- /dev/null
+++ b/syn-mid/src/lib.rs
@@ -0,0 +1,190 @@
+//! 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.4.0")]
+#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))]
+#![warn(unsafe_code)]
+#![warn(rust_2018_idioms, unreachable_pub)]
+#![warn(single_use_lifetimes)]
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::eval_order_dependence,
+ clippy::large_enum_variant,
+ clippy::module_name_repetitions,
+ clippy::use_self
+)]
+
+// 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::*;
+pub use self::pat::*;
+
+use proc_macro2::TokenStream;
+use syn::{
+ punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, 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 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 output: ReturnType,
+ pub block: Block,
+ }
+}
+
+mod parsing {
+ use syn::{
+ braced, parenthesized,
+ parse::{Parse, ParseStream, Result},
+ Abi, Attribute, Generics, Ident, ReturnType, Token, Visibility, WhereClause,
+ };
+
+ use super::{Block, FnArg, ItemFn};
+
+ 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 output: ReturnType = input.parse()?;
+ let where_clause: Option<WhereClause> = input.parse()?;
+
+ let block = input.parse()?;
+
+ Ok(Self {
+ attrs,
+ vis,
+ constness,
+ asyncness,
+ unsafety,
+ abi,
+ fn_token,
+ ident,
+ generics: Generics {
+ where_clause,
+ ..generics
+ },
+ paren_token,
+ inputs,
+ output,
+ block,
+ })
+ }
+ }
+}
+
+mod printing {
+ use proc_macro2::TokenStream;
+ use quote::{ToTokens, TokenStreamExt};
+
+ use super::{Block, ItemFn};
+
+ impl ToTokens for Block {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.brace_token.surround(tokens, |tokens| {
+ tokens.append_all(self.stmts.clone());
+ });
+ }
+ }
+
+ impl ToTokens for ItemFn {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append_all(&self.attrs);
+ self.vis.to_tokens(tokens);
+ 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);
+ });
+ self.output.to_tokens(tokens);
+ self.generics.where_clause.to_tokens(tokens);
+ self.block.brace_token.surround(tokens, |tokens| {
+ tokens.append_all(self.block.stmts.clone());
+ });
+ }
+ }
+}
diff --git a/syn-mid/src/macros.rs b/syn-mid/src/macros.rs
new file mode 100644
index 0000000..87be7b4
--- /dev/null
+++ b/syn-mid/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/syn-mid/src/pat.rs b/syn-mid/src/pat.rs
new file mode 100644
index 0000000..8f95381
--- /dev/null
+++ b/syn-mid/src/pat.rs
@@ -0,0 +1,413 @@
+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/syn-mid/src/path.rs b/syn-mid/src/path.rs
new file mode 100644
index 0000000..8093b53
--- /dev/null
+++ b/syn-mid/src/path.rs
@@ -0,0 +1,50 @@
+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
+ },
+ })
+}