diff --git a/.cargo-ok b/.cargo-ok deleted file mode 100644 index b5754e2..0000000 --- a/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -ok \ No newline at end of file diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json deleted file mode 100644 index 4cedf6c..0000000 --- a/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "b01743f24cb5b19f96a3eac6bce0e7aee10f6199" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..3d30690 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.31.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed04303..5e67515 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,6 @@ name: CI on: push: pull_request: - workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: @@ -13,58 +12,45 @@ env: RUSTFLAGS: -Dwarnings jobs: - pre_ci: - uses: dtolnay/.github/.github/workflows/pre_ci.yml@master - test: name: Rust ${{matrix.rust}} - needs: pre_ci - if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest strategy: fail-fast: false matrix: - rust: [nightly, stable, beta, 1.56.0] + rust: [stable, beta, 1.56.0] timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} components: rust-src - - name: Enable type layout randomization - run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV - if: matrix.rust == 'nightly' - run: cargo test - - run: cargo run --manifest-path benches/Cargo.toml - minimal: - name: Minimal versions - needs: pre_ci - if: needs.pre_ci.outputs.continue + nightly: + name: Rust nightly runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - run: cargo generate-lockfile -Z minimal-versions - - run: cargo check --locked - - doc: - name: Documentation - needs: pre_ci - if: needs.pre_ci.outputs.continue - runs-on: ubuntu-latest - timeout-minutes: 45 - env: - RUSTDOCFLAGS: -Dwarnings - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly with: components: rust-src - - uses: dtolnay/install@cargo-docs-rs - - run: cargo docs-rs + - run: cargo test + - run: cargo update -Z minimal-versions + - run: cargo build + + msrv: + name: Rust 1.31.0 + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.31.0 + with: + components: rust-src + - run: cargo check clippy: name: Clippy @@ -72,22 +58,19 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly with: components: clippy, rust-src - - run: cargo clippy --tests --workspace -- -Dclippy::all -Dclippy::pedantic + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic miri: name: Miri - needs: pre_ci - if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@miri - - run: cargo miri setup - run: cargo miri test env: MIRIFLAGS: -Zmiri-strict-provenance @@ -98,6 +81,6 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 diff --git a/BUILD.gn b/BUILD.gn index 944eac3..2faa46b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -14,16 +14,18 @@ import("//build/ohos.gni") ohos_cargo_crate("lib") { - crate_name = "quote" - crate_type = "rlib" - crate_root = "src/lib.rs" + crate_name = "quote" + crate_type = "rlib" + crate_root = "src/lib.rs" - sources = [ "src/lib.rs" ] - edition = "2018" - cargo_pkg_version = "1.0.35" - cargo_pkg_authors = "David Tolnay " - cargo_pkg_name = "quote" - cargo_pkg_description = "Quasi-quoting macro quote!(...)" - deps = [ "//third_party/rust/crates/proc-macro2:lib" ] - features = [ "proc-macro" ] + sources = ["src/lib.rs"] + edition = "2018" + cargo_pkg_version = "1.0.23" + cargo_pkg_authors = "David Tolnay " + cargo_pkg_name = "quote" + cargo_pkg_description = "Quasi-quoting macro quote!(...)" + deps = ["//third_party/rust/crates/proc-macro2:lib"] + features = ["proc-macro"] + build_root = "build.rs" + build_sources = ["build.rs"] } diff --git a/Cargo.toml b/Cargo.toml index f3222c2..f99b48f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,50 +1,35 @@ -# 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 are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - [package] -edition = "2018" -rust-version = "1.56" name = "quote" -version = "1.0.35" +version = "1.0.23" # don't forget to update html_root_url, version in readme for breaking changes authors = ["David Tolnay "] autobenches = false +categories = ["development-tools::procedural-macro-helpers"] description = "Quasi-quoting macro quote!(...)" documentation = "https://docs.rs/quote/" -readme = "README.md" -keywords = [ - "macros", - "syn", -] -categories = ["development-tools::procedural-macro-helpers"] +edition = "2018" +keywords = ["macros", "syn"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/quote" +rust-version = "1.31" -[package.metadata.docs.rs] -rustdoc-args = ["--generate-link-to-definition"] -targets = ["x86_64-unknown-linux-gnu"] +[dependencies] +proc-macro2 = { version = "1.0.40", default-features = false } + +[dev-dependencies] +rustversion = "1.0" +trybuild = { version = "1.0.66", features = ["diff"] } + +[features] +default = ["proc-macro"] +# Disabling the proc-macro feature removes the dynamic library dependency on +# libproc_macro in the rustc compiler. +proc-macro = ["proc-macro2/proc-macro"] [lib] doc-scrape-examples = false -[dependencies.proc-macro2] -version = "1.0.74" -default-features = false +[workspace] +members = ["benches"] -[dev-dependencies.rustversion] -version = "1.0" - -[dev-dependencies.trybuild] -version = "1.0.66" -features = ["diff"] - -[features] -default = ["proc-macro"] -proc-macro = ["proc-macro2/proc-macro"] +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig deleted file mode 100644 index 33d9865..0000000 --- a/Cargo.toml.orig +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "quote" -version = "1.0.35" # don't forget to update html_root_url, version in readme for breaking changes -authors = ["David Tolnay "] -autobenches = false -categories = ["development-tools::procedural-macro-helpers"] -description = "Quasi-quoting macro quote!(...)" -documentation = "https://docs.rs/quote/" -edition = "2018" -keywords = ["macros", "syn"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/dtolnay/quote" -rust-version = "1.56" - -[dependencies] -proc-macro2 = { version = "1.0.74", default-features = false } - -[dev-dependencies] -rustversion = "1.0" -trybuild = { version = "1.0.66", features = ["diff"] } - -[features] -default = ["proc-macro"] -# Disabling the proc-macro feature removes the dynamic library dependency on -# libproc_macro in the rustc compiler. -proc-macro = ["proc-macro2/proc-macro"] - -[lib] -doc-scrape-examples = false - -[workspace] -members = ["benches"] - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 1b5ec8b..16fe87b 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -174,3 +174,28 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 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/README.OpenSource b/README.OpenSource index b40ea85..ce1063e 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "quote", "License": "Apache License V2.0, MIT", "License File": "LICENSE-APACHE, LICENSE-MIT", - "Version Number": "1.0.35", + "Version Number": "1.0.23", "Owner": "fangting12@huawei.com", "Upstream URL": "https://github.com/dtolnay/quote", "Description": "A Rust library that provides support for generating Rust code." diff --git a/README.md b/README.md index bfc91a9..74e99ce 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ macros. quote = "1.0" ``` -*Version requirement: Quote supports rustc 1.56 and up.*
+*Version requirement: Quote supports rustc 1.31 and up.*
[*Release notes*](https://github.com/dtolnay/quote/releases)
@@ -233,26 +233,15 @@ macro. ## Non-macro code generators When using `quote` in a build.rs or main.rs and writing the output out to a -file, consider having the code generator pass the tokens through [prettyplease] -before writing. This way if an error occurs in the generated code it is -convenient for a human to read and debug. +file, consider having the code generator pass the tokens through [rustfmt] +before writing (either by shelling out to the `rustfmt` binary or by pulling in +the `rustfmt` library as a dependency). This way if an error occurs in the +generated code it is convenient for a human to read and debug. Be aware that no kind of hygiene or span information is retained when tokens are written to a file; the conversion from tokens to source code is lossy. -Example usage in build.rs: - -```rust -let output = quote! { ... }; -let syntax_tree = syn::parse2(output).unwrap(); -let formatted = prettyplease::unparse(&syntax_tree); - -let out_dir = env::var_os("OUT_DIR").unwrap(); -let dest_path = Path::new(&out_dir).join("out.rs"); -fs::write(dest_path, formatted).unwrap(); -``` - -[prettyplease]: https://github.com/dtolnay/prettyplease +[rustfmt]: https://github.com/rust-lang/rustfmt
diff --git a/benches/Cargo.toml b/benches/Cargo.toml new file mode 100644 index 0000000..366f9df --- /dev/null +++ b/benches/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "quote-benchmark" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +publish = false + +[lib] +proc-macro = true +path = "lib.rs" + +[[bin]] +name = "quote-benchmark" +path = "main.rs" + +[dependencies] +proc-macro2 = "1.0" +quote = { path = ".." } +termcolor = "1.1" diff --git a/benches/README.md b/benches/README.md new file mode 100644 index 0000000..2393ed1 --- /dev/null +++ b/benches/README.md @@ -0,0 +1,18 @@ +Example output: + +
+$ cargo run && cargo run --release
+
+   Compiling quote v1.0.10
+   Compiling quote-benchmark v0.0.0
+macro in debug mode: 1655 micros
+    Finished dev [unoptimized + debuginfo] target(s) in 4.39s
+     Running `/git/quote/target/debug/quote-benchmark`
+non-macro in debug mode: 1205 micros
+   Compiling quote v1.0.10
+   Compiling quote-benchmark v0.0.0
+macro in release mode: 1635 micros
+    Finished release [optimized] target(s) in 4.00s
+     Running `/git/quote/target/release/quote-benchmark`
+non-macro in release mode: 105 micros
+
diff --git a/benches/lib.rs b/benches/lib.rs new file mode 100644 index 0000000..f194165 --- /dev/null +++ b/benches/lib.rs @@ -0,0 +1,206 @@ +use quote::quote; + +#[allow(unused_macros)] +macro_rules! benchmark { + (|$ident:ident| $quote:expr) => { + mod timer; + + use proc_macro::TokenStream; + use proc_macro2::Ident; + + #[proc_macro] + pub fn run_quote_benchmark(input: TokenStream) -> TokenStream { + let input = proc_macro2::TokenStream::from(input); + let span = input.into_iter().next().unwrap().span(); + let $ident = Ident::new("Response", span); + timer::time("macro", || proc_macro::TokenStream::from($quote)); + TokenStream::new() + } + }; +} + +#[allow(unused_imports)] +use benchmark; + +crate::benchmark! { + |ident| quote! { + impl<'de> _serde::Deserialize<'de> for #ident { + fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + enum __Field { + __field0, + __field1, + __ignore, + } + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::export::Formatter, + ) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(__formatter, "field identifier") + } + fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::export::Ok(__Field::__field0), + 1u64 => _serde::export::Ok(__Field::__field1), + _ => _serde::export::Err(_serde::de::Error::invalid_value( + _serde::de::Unexpected::Unsigned(__value), + &"field index 0 <= i < 2", + )), + } + } + fn visit_str<__E>(self, __value: &str) -> _serde::export::Result + where + __E: _serde::de::Error, + { + match __value { + "id" => _serde::export::Ok(__Field::__field0), + "s" => _serde::export::Ok(__Field::__field1), + _ => _serde::export::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::export::Result + where + __E: _serde::de::Error, + { + match __value { + b"id" => _serde::export::Ok(__Field::__field0), + b"s" => _serde::export::Ok(__Field::__field1), + _ => _serde::export::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor) + } + } + struct __Visitor<'de> { + marker: _serde::export::PhantomData<#ident>, + lifetime: _serde::export::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = #ident; + fn expecting( + &self, + __formatter: &mut _serde::export::Formatter, + ) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(__formatter, "struct") + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::export::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = + match try!(_serde::de::SeqAccess::next_element::(&mut __seq)) { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length( + 0usize, + &"struct with 2 elements", + )); + } + }; + let __field1 = + match try!(_serde::de::SeqAccess::next_element::(&mut __seq)) { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length( + 1usize, + &"struct with 2 elements", + )); + } + }; + _serde::export::Ok(#ident { + id: __field0, + s: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::export::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::export::Option = _serde::export::None; + let mut __field1: _serde::export::Option = _serde::export::None; + while let _serde::export::Some(__key) = + try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) + { + match __key { + __Field::__field0 => { + if _serde::export::Option::is_some(&__field0) { + return _serde::export::Err( + <__A::Error as _serde::de::Error>::duplicate_field("id"), + ); + } + __field0 = _serde::export::Some( + try!(_serde::de::MapAccess::next_value::(&mut __map)), + ); + } + __Field::__field1 => { + if _serde::export::Option::is_some(&__field1) { + return _serde::export::Err( + <__A::Error as _serde::de::Error>::duplicate_field("s"), + ); + } + __field1 = _serde::export::Some( + try!(_serde::de::MapAccess::next_value::(&mut __map)), + ); + } + _ => { + let _ = try!(_serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)); + } + } + } + let __field0 = match __field0 { + _serde::export::Some(__field0) => __field0, + _serde::export::None => try!(_serde::private::de::missing_field("id")), + }; + let __field1 = match __field1 { + _serde::export::Some(__field1) => __field1, + _serde::export::None => try!(_serde::private::de::missing_field("s")), + }; + _serde::export::Ok(#ident { + id: __field0, + s: __field1, + }) + } + } + const FIELDS: &'static [&'static str] = &["id", "s"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + stringify!(#ident), + FIELDS, + __Visitor { + marker: _serde::export::PhantomData::<#ident>, + lifetime: _serde::export::PhantomData, + }, + ) + } + } + } +} diff --git a/benches/main.rs b/benches/main.rs new file mode 100644 index 0000000..8cfbcb6 --- /dev/null +++ b/benches/main.rs @@ -0,0 +1,25 @@ +quote_benchmark::run_quote_benchmark!(_); + +mod benchmark { + macro_rules! benchmark { + (|$ident:ident| $quote:expr) => { + use proc_macro2::{Ident, Span}; + + pub fn quote() -> proc_macro2::TokenStream { + let $ident = Ident::new("Response", Span::call_site()); + $quote + } + }; + } + + pub(crate) use benchmark; +} + +use benchmark::benchmark; + +mod lib; +mod timer; + +fn main() { + timer::time("non-macro", lib::quote); +} diff --git a/benches/timer.rs b/benches/timer.rs new file mode 100644 index 0000000..cc1e426 --- /dev/null +++ b/benches/timer.rs @@ -0,0 +1,17 @@ +use std::io::Write; +use std::time::Instant; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; + +const ITERATIONS: u32 = 1000; + +pub fn time(name: &'static str, function: impl Fn() -> T) { + let begin = Instant::now(); + for _ in 0..ITERATIONS { + _ = function(); + } + let micros = (begin.elapsed() / ITERATIONS).as_micros(); + let mode = ["release", "debug"][cfg!(debug_assertions) as usize]; + let mut writer = StandardStream::stderr(ColorChoice::Auto); + _ = writer.set_color(ColorSpec::new().set_fg(Some(Color::Magenta))); + _ = writeln!(&mut writer, "{} in {} mode: {} micros", name, mode, micros); +} diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a7e6b2c --- /dev/null +++ b/build.rs @@ -0,0 +1,38 @@ +use std::env; +use std::process::{self, Command}; +use std::str; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let version = match rustc_version() { + Some(version) => version, + None => return, + }; + + if version.minor < 31 { + eprintln!("Minimum supported rustc version is 1.31"); + process::exit(1); + } + + if version.minor < 53 { + // https://github.com/rust-lang/rust/issues/43081 + println!("cargo:rustc-cfg=needs_invalid_span_workaround"); + } +} + +struct RustcVersion { + minor: u32, +} + +fn rustc_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + let minor = pieces.next()?.parse().ok()?; + Some(RustcVersion { minor }) +} diff --git a/src/ident_fragment.rs b/src/ident_fragment.rs index 6c2a9a8..cf74024 100644 --- a/src/ident_fragment.rs +++ b/src/ident_fragment.rs @@ -1,6 +1,6 @@ -use alloc::borrow::Cow; use core::fmt; use proc_macro2::{Ident, Span}; +use std::borrow::Cow; /// Specialized formatting trait used by `format_ident!`. /// @@ -8,8 +8,6 @@ use proc_macro2::{Ident, Span}; /// stripped, if present. /// /// See [`format_ident!`] for more information. -/// -/// [`format_ident!`]: crate::format_ident pub trait IdentFragment { /// Format this value as an identifier fragment. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result; @@ -49,8 +47,8 @@ impl IdentFragment for Ident { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let id = self.to_string(); - if let Some(id) = id.strip_prefix("r#") { - fmt::Display::fmt(id, f) + if id.starts_with("r#") { + fmt::Display::fmt(&id[2..], f) } else { fmt::Display::fmt(&id[..], f) } diff --git a/src/lib.rs b/src/lib.rs index 8b97abd..adc14c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,20 +79,9 @@ //! } //! }; //! ``` -//! -//!
-//! -//! # Non-macro code generators -//! -//! When using `quote` in a build.rs or main.rs and writing the output out to a -//! file, consider having the code generator pass the tokens through -//! [prettyplease] before writing. This way if an error occurs in the generated -//! code it is convenient for a human to read and debug. -//! -//! [prettyplease]: https://github.com/dtolnay/prettyplease // Quote types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/quote/1.0.35")] +#![doc(html_root_url = "https://docs.rs/quote/1.0.23")] #![allow( clippy::doc_markdown, clippy::missing_errors_doc, @@ -102,9 +91,10 @@ clippy::wrong_self_convention, )] -extern crate alloc; - -#[cfg(feature = "proc-macro")] +#[cfg(all( + not(all(target_arch = "wasm32", target_os = "unknown")), + feature = "proc-macro" +))] extern crate proc_macro; mod ext; @@ -428,7 +418,7 @@ pub mod spanned; /// appears suffixed as integer literals by interpolating them as [`syn::Index`] /// instead. /// -/// [`syn::Index`]: https://docs.rs/syn/2.0/syn/struct.Index.html +/// [`syn::Index`]: https://docs.rs/syn/1.0/syn/struct.Index.html /// /// ```compile_fail /// let i = 0usize..self.fields.len(); @@ -629,14 +619,14 @@ macro_rules! quote_spanned { #[macro_export] macro_rules! quote_spanned { ($span:expr=>) => {{ - let _: $crate::__private::Span = $crate::__private::get_span($span).__into_span(); + let _: $crate::__private::Span = $span; $crate::__private::TokenStream::new() }}; // Special case rule for a single tt, for performance. ($span:expr=> $tt:tt) => {{ let mut _s = $crate::__private::TokenStream::new(); - let _span: $crate::__private::Span = $crate::__private::get_span($span).__into_span(); + let _span: $crate::__private::Span = $span; $crate::quote_token_spanned!{$tt _s _span} _s }}; @@ -644,13 +634,13 @@ macro_rules! quote_spanned { // Special case rules for two tts, for performance. ($span:expr=> # $var:ident) => {{ let mut _s = $crate::__private::TokenStream::new(); - let _: $crate::__private::Span = $crate::__private::get_span($span).__into_span(); + let _: $crate::__private::Span = $span; $crate::ToTokens::to_tokens(&$var, &mut _s); _s }}; ($span:expr=> $tt1:tt $tt2:tt) => {{ let mut _s = $crate::__private::TokenStream::new(); - let _span: $crate::__private::Span = $crate::__private::get_span($span).__into_span(); + let _span: $crate::__private::Span = $span; $crate::quote_token_spanned!{$tt1 _s _span} $crate::quote_token_spanned!{$tt2 _s _span} _s @@ -659,7 +649,7 @@ macro_rules! quote_spanned { // Rule for any other number of tokens. ($span:expr=> $($tt:tt)*) => {{ let mut _s = $crate::__private::TokenStream::new(); - let _span: $crate::__private::Span = $crate::__private::get_span($span).__into_span(); + let _span: $crate::__private::Span = $span; $crate::quote_each_token_spanned!{_s _span $($tt)*} _s }}; diff --git a/src/runtime.rs b/src/runtime.rs index eff044a..f3cdded 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,25 +1,13 @@ -use self::get_span::{GetSpan, GetSpanBase, GetSpanInner}; use crate::{IdentFragment, ToTokens, TokenStreamExt}; use core::fmt; use core::iter; use core::ops::BitOr; -use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree}; -#[doc(hidden)] -pub use alloc::format; -#[doc(hidden)] pub use core::option::Option; +pub use proc_macro2::*; +pub use std::format; -#[doc(hidden)] -pub type Delimiter = proc_macro2::Delimiter; -#[doc(hidden)] -pub type Span = proc_macro2::Span; -#[doc(hidden)] -pub type TokenStream = proc_macro2::TokenStream; - -#[doc(hidden)] pub struct HasIterator; // True -#[doc(hidden)] pub struct ThereIsNoIteratorInRepetition; // False impl BitOr for ThereIsNoIteratorInRepetition { @@ -56,16 +44,14 @@ impl BitOr for HasIterator { /// These traits expose a `quote_into_iter` method which should allow calling /// whichever impl happens to be applicable. Calling that method repeatedly on /// the returned value should be idempotent. -#[doc(hidden)] pub mod ext { use super::RepInterp; use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter}; use crate::ToTokens; - use alloc::collections::btree_set::{self, BTreeSet}; use core::slice; + use std::collections::btree_set::{self, BTreeSet}; /// Extension trait providing the `quote_into_iter` method on iterators. - #[doc(hidden)] pub trait RepIteratorExt: Iterator + Sized { fn quote_into_iter(self) -> (Self, HasIter) { (self, HasIter) @@ -77,7 +63,6 @@ pub mod ext { /// Extension trait providing the `quote_into_iter` method for /// non-iterable types. These types interpolate the same value in each /// iteration of the repetition. - #[doc(hidden)] pub trait RepToTokensExt { /// Pretend to be an iterator for the purposes of `quote_into_iter`. /// This allows repeated calls to `quote_into_iter` to continue @@ -95,7 +80,6 @@ pub mod ext { /// Extension trait providing the `quote_into_iter` method for types that /// can be referenced as an iterator. - #[doc(hidden)] pub trait RepAsIteratorExt<'q> { type Iter: Iterator; @@ -154,7 +138,6 @@ pub mod ext { // Helper type used within interpolations to allow for repeated binding names. // Implements the relevant traits, and exports a dummy `next()` method. #[derive(Copy, Clone)] -#[doc(hidden)] pub struct RepInterp(pub T); impl RepInterp { @@ -181,69 +164,10 @@ impl ToTokens for RepInterp { } } -#[doc(hidden)] -#[inline] -pub fn get_span(span: T) -> GetSpan { - GetSpan(GetSpanInner(GetSpanBase(span))) -} - -mod get_span { - use core::ops::Deref; - use proc_macro2::extra::DelimSpan; - use proc_macro2::Span; - - pub struct GetSpan(pub(crate) GetSpanInner); - - pub struct GetSpanInner(pub(crate) GetSpanBase); - - pub struct GetSpanBase(pub(crate) T); - - impl GetSpan { - #[inline] - pub fn __into_span(self) -> Span { - ((self.0).0).0 - } - } - - impl GetSpanInner { - #[inline] - pub fn __into_span(&self) -> Span { - (self.0).0.join() - } - } - - impl GetSpanBase { - #[allow(clippy::unused_self)] - pub fn __into_span(&self) -> T { - unreachable!() - } - } - - impl Deref for GetSpan { - type Target = GetSpanInner; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl Deref for GetSpanInner { - type Target = GetSpanBase; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } - } -} - -#[doc(hidden)] pub fn push_group(tokens: &mut TokenStream, delimiter: Delimiter, inner: TokenStream) { tokens.append(Group::new(delimiter, inner)); } -#[doc(hidden)] pub fn push_group_spanned( tokens: &mut TokenStream, span: Span, @@ -255,13 +179,11 @@ pub fn push_group_spanned( tokens.append(g); } -#[doc(hidden)] pub fn parse(tokens: &mut TokenStream, s: &str) { let s: TokenStream = s.parse().expect("invalid token stream"); tokens.extend(iter::once(s)); } -#[doc(hidden)] pub fn parse_spanned(tokens: &mut TokenStream, span: Span, s: &str) { let s: TokenStream = s.parse().expect("invalid token stream"); tokens.extend(s.into_iter().map(|t| respan_token_tree(t, span))); @@ -284,18 +206,15 @@ fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { token } -#[doc(hidden)] pub fn push_ident(tokens: &mut TokenStream, s: &str) { let span = Span::call_site(); push_ident_spanned(tokens, span, s); } -#[doc(hidden)] pub fn push_ident_spanned(tokens: &mut TokenStream, span: Span, s: &str) { tokens.append(ident_maybe_raw(s, span)); } -#[doc(hidden)] pub fn push_lifetime(tokens: &mut TokenStream, lifetime: &str) { struct Lifetime<'a> { name: &'a str, @@ -326,7 +245,6 @@ pub fn push_lifetime(tokens: &mut TokenStream, lifetime: &str) { }); } -#[doc(hidden)] pub fn push_lifetime_spanned(tokens: &mut TokenStream, span: Span, lifetime: &str) { struct Lifetime<'a> { name: &'a str, @@ -363,11 +281,9 @@ pub fn push_lifetime_spanned(tokens: &mut TokenStream, span: Span, lifetime: &st macro_rules! push_punct { ($name:ident $spanned:ident $char1:tt) => { - #[doc(hidden)] pub fn $name(tokens: &mut TokenStream) { tokens.append(Punct::new($char1, Spacing::Alone)); } - #[doc(hidden)] pub fn $spanned(tokens: &mut TokenStream, span: Span) { let mut punct = Punct::new($char1, Spacing::Alone); punct.set_span(span); @@ -375,12 +291,10 @@ macro_rules! push_punct { } }; ($name:ident $spanned:ident $char1:tt $char2:tt) => { - #[doc(hidden)] pub fn $name(tokens: &mut TokenStream) { tokens.append(Punct::new($char1, Spacing::Joint)); tokens.append(Punct::new($char2, Spacing::Alone)); } - #[doc(hidden)] pub fn $spanned(tokens: &mut TokenStream, span: Span) { let mut punct = Punct::new($char1, Spacing::Joint); punct.set_span(span); @@ -391,13 +305,11 @@ macro_rules! push_punct { } }; ($name:ident $spanned:ident $char1:tt $char2:tt $char3:tt) => { - #[doc(hidden)] pub fn $name(tokens: &mut TokenStream) { tokens.append(Punct::new($char1, Spacing::Joint)); tokens.append(Punct::new($char2, Spacing::Joint)); tokens.append(Punct::new($char3, Spacing::Alone)); } - #[doc(hidden)] pub fn $spanned(tokens: &mut TokenStream, span: Span) { let mut punct = Punct::new($char1, Spacing::Joint); punct.set_span(span); @@ -457,27 +369,24 @@ push_punct!(push_star push_star_spanned '*'); push_punct!(push_sub push_sub_spanned '-'); push_punct!(push_sub_eq push_sub_eq_spanned '-' '='); -#[doc(hidden)] pub fn push_underscore(tokens: &mut TokenStream) { push_underscore_spanned(tokens, Span::call_site()); } -#[doc(hidden)] pub fn push_underscore_spanned(tokens: &mut TokenStream, span: Span) { tokens.append(Ident::new("_", span)); } // Helper method for constructing identifiers from the `format_ident!` macro, // handling `r#` prefixes. -#[doc(hidden)] pub fn mk_ident(id: &str, span: Option) -> Ident { let span = span.unwrap_or_else(Span::call_site); ident_maybe_raw(id, span) } fn ident_maybe_raw(id: &str, span: Span) -> Ident { - if let Some(id) = id.strip_prefix("r#") { - Ident::new_raw(id, span) + if id.starts_with("r#") { + Ident::new_raw(&id[2..], span) } else { Ident::new(id, span) } @@ -490,7 +399,6 @@ fn ident_maybe_raw(id: &str, span: Span) -> Ident { // `Octal`, `LowerHex`, `UpperHex`, and `Binary` to allow for their use within // `format_ident!`. #[derive(Copy, Clone)] -#[doc(hidden)] pub struct IdentFragmentAdapter(pub T); impl IdentFragmentAdapter { diff --git a/src/spanned.rs b/src/spanned.rs index 6eba644..f64c8f5 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -1,9 +1,7 @@ use crate::ToTokens; -use proc_macro2::extra::DelimSpan; use proc_macro2::{Span, TokenStream}; -// Not public API other than via the syn crate. Use syn::spanned::Spanned. -pub trait Spanned: private::Sealed { +pub trait Spanned { fn __span(&self) -> Span; } @@ -13,12 +11,6 @@ impl Spanned for Span { } } -impl Spanned for DelimSpan { - fn __span(&self) -> Span { - self.join() - } -} - impl Spanned for T { fn __span(&self) -> Span { join_spans(self.into_token_stream()) @@ -26,8 +18,20 @@ impl Spanned for T { } fn join_spans(tokens: TokenStream) -> Span { + #[cfg(not(needs_invalid_span_workaround))] let mut iter = tokens.into_iter().map(|tt| tt.span()); + #[cfg(needs_invalid_span_workaround)] + let mut iter = tokens.into_iter().filter_map(|tt| { + let span = tt.span(); + let debug = format!("{:?}", span); + if debug.ends_with("bytes(0..0)") { + None + } else { + Some(span) + } + }); + let first = match iter.next() { Some(span) => span, None => return Span::call_site(), @@ -37,14 +41,3 @@ fn join_spans(tokens: TokenStream) -> Span { .and_then(|last| first.join(last)) .unwrap_or(first) } - -mod private { - use crate::ToTokens; - use proc_macro2::extra::DelimSpan; - use proc_macro2::Span; - - pub trait Sealed {} - impl Sealed for Span {} - impl Sealed for DelimSpan {} - impl Sealed for T {} -} diff --git a/src/to_tokens.rs b/src/to_tokens.rs index 23b6ec2..5748721 100644 --- a/src/to_tokens.rs +++ b/src/to_tokens.rs @@ -1,8 +1,8 @@ use super::TokenStreamExt; -use alloc::borrow::Cow; -use alloc::rc::Rc; use core::iter; use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; +use std::borrow::Cow; +use std::rc::Rc; /// Types that can be interpolated inside a `quote!` invocation. /// diff --git a/tests/test.rs b/tests/test.rs index eab4f55..52ec7bc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,17 +1,14 @@ #![allow( clippy::disallowed_names, - clippy::let_underscore_untyped, clippy::shadow_unrelated, clippy::unseparated_literal_suffix, clippy::used_underscore_binding )] -extern crate proc_macro; - use std::borrow::Cow; use std::collections::BTreeSet; -use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, TokenStreamExt}; struct X; @@ -520,30 +517,3 @@ fn test_quote_raw_id() { let id = quote!(r#raw_id); assert_eq!(id.to_string(), "r#raw_id"); } - -#[test] -fn test_type_inference_for_span() { - trait CallSite { - fn get() -> Self; - } - - impl CallSite for Span { - fn get() -> Self { - Span::call_site() - } - } - - let span = Span::call_site(); - let _ = quote_spanned!(span=> ...); - - let delim_span = Group::new(Delimiter::Parenthesis, TokenStream::new()).delim_span(); - let _ = quote_spanned!(delim_span=> ...); - - let inferred = CallSite::get(); - let _ = quote_spanned!(inferred=> ...); - - if false { - let proc_macro_span = proc_macro::Span::call_site(); - let _ = quote_spanned!(proc_macro_span.into()=> ...); - } -} diff --git a/tests/ui/does-not-have-iter-interpolated-dup.stderr b/tests/ui/does-not-have-iter-interpolated-dup.stderr index 99c20a5..8087879 100644 --- a/tests/ui/does-not-have-iter-interpolated-dup.stderr +++ b/tests/ui/does-not-have-iter-interpolated-dup.stderr @@ -4,8 +4,7 @@ error[E0308]: mismatched types 8 | quote!(#(#nonrep #nonrep)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected struct `HasIterator`, found struct `ThereIsNoIteratorInRepetition` | expected due to this - | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` | = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter-interpolated.stderr b/tests/ui/does-not-have-iter-interpolated.stderr index ef90813..2dcf206 100644 --- a/tests/ui/does-not-have-iter-interpolated.stderr +++ b/tests/ui/does-not-have-iter-interpolated.stderr @@ -4,8 +4,7 @@ error[E0308]: mismatched types 8 | quote!(#(#nonrep)*); | ^^^^^^^^^^^^^^^^^^^ | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected struct `HasIterator`, found struct `ThereIsNoIteratorInRepetition` | expected due to this - | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` | = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter-separated.stderr b/tests/ui/does-not-have-iter-separated.stderr index 7c6e30f..87a19fe 100644 --- a/tests/ui/does-not-have-iter-separated.stderr +++ b/tests/ui/does-not-have-iter-separated.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types 4 | quote!(#(a b),*); | ^^^^^^^^^^^^^^^^ | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected struct `HasIterator`, found struct `ThereIsNoIteratorInRepetition` | expected due to this | = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter.stderr b/tests/ui/does-not-have-iter.stderr index 0b13e5c..32aa62d 100644 --- a/tests/ui/does-not-have-iter.stderr +++ b/tests/ui/does-not-have-iter.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types 4 | quote!(#(a b)*); | ^^^^^^^^^^^^^^^ | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected struct `HasIterator`, found struct `ThereIsNoIteratorInRepetition` | expected due to this | = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/not-quotable.rs b/tests/ui/not-quotable.rs index f991c18..220542d 100644 --- a/tests/ui/not-quotable.rs +++ b/tests/ui/not-quotable.rs @@ -3,5 +3,5 @@ use std::net::Ipv4Addr; fn main() { let ip = Ipv4Addr::LOCALHOST; - let _ = quote! { #ip }; + _ = quote! { #ip }; } diff --git a/tests/ui/not-quotable.stderr b/tests/ui/not-quotable.stderr index 35cb6f2..c323d99 100644 --- a/tests/ui/not-quotable.stderr +++ b/tests/ui/not-quotable.stderr @@ -1,20 +1,20 @@ error[E0277]: the trait bound `Ipv4Addr: ToTokens` is not satisfied - --> tests/ui/not-quotable.rs:6:13 + --> tests/ui/not-quotable.rs:6:9 | -6 | let _ = quote! { #ip }; - | ^^^^^^^^^^^^^^ - | | - | the trait `ToTokens` is not implemented for `Ipv4Addr` - | required by a bound introduced by this call +6 | _ = quote! { #ip }; + | ^^^^^^^^^^^^^^ + | | + | the trait `ToTokens` is not implemented for `Ipv4Addr` + | required by a bound introduced by this call | = help: the following other types implement trait `ToTokens`: - bool - char - isize - i8 - i16 - i32 - i64 - i128 - and $N others + &'a T + &'a mut T + Box + Cow<'a, T> + Option + Rc + RepInterp + String + and 23 others = note: this error originates in the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/not-repeatable.rs b/tests/ui/not-repeatable.rs index a8f0fe7..c1debf5 100644 --- a/tests/ui/not-repeatable.rs +++ b/tests/ui/not-repeatable.rs @@ -4,5 +4,5 @@ struct Ipv4Addr; fn main() { let ip = Ipv4Addr; - let _ = quote! { #(#ip)* }; + _ = quote! { #(#ip)* }; } diff --git a/tests/ui/not-repeatable.stderr b/tests/ui/not-repeatable.stderr index 2ed1da0..264a89f 100644 --- a/tests/ui/not-repeatable.stderr +++ b/tests/ui/not-repeatable.stderr @@ -1,5 +1,5 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied - --> tests/ui/not-repeatable.rs:7:13 + --> tests/ui/not-repeatable.rs:7:9 | 3 | struct Ipv4Addr; | --------------- @@ -10,8 +10,8 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its | doesn't satisfy `Ipv4Addr: ext::RepIteratorExt` | doesn't satisfy `Ipv4Addr: ext::RepToTokensExt` ... -7 | let _ = quote! { #(#ip)* }; - | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds +7 | _ = quote! { #(#ip)* }; + | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `Ipv4Addr: Iterator` diff --git a/tests/ui/wrong-type-span.stderr b/tests/ui/wrong-type-span.stderr index 12ad307..c774a4c 100644 --- a/tests/ui/wrong-type-span.stderr +++ b/tests/ui/wrong-type-span.stderr @@ -1,10 +1,8 @@ error[E0308]: mismatched types - --> tests/ui/wrong-type-span.rs:6:5 + --> tests/ui/wrong-type-span.rs:6:20 | 6 | quote_spanned!(span=> #x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `Span`, found `&str` + | ---------------^^^^------ + | | | + | | expected struct `Span`, found `&str` | expected due to this - | - = note: this error originates in the macro `quote_spanned` (in Nightly builds, run with -Z macro-backtrace for more info)