aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-07-28 00:41:09 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-07-28 00:41:09 +0000
commite4ed7fa7ff44003a777fbc882bac61f95007dc18 (patch)
tree75f1ad89d6cebfb3f52ca64b4bdefb8c004c2453
parentb78dc1f5aa154d67f58e478ca01e557053cd9ceb (diff)
parentffccca4fd917627336372c2cd613c1f6d775d60f (diff)
downloadpaste-e4ed7fa7ff44003a777fbc882bac61f95007dc18.tar.gz
Upgrade rust/crates/paste to 1.0.0 am: 4117715894 am: ffccca4fd9
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/paste/+/1372673 Change-Id: If19822a80dc3befca34aac3ae11dfcd265aa16a2
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/ci.yml23
-rw-r--r--Android.bp13
-rw-r--r--Cargo.toml8
-rw-r--r--Cargo.toml.orig10
-rw-r--r--METADATA4
-rw-r--r--README.md72
-rw-r--r--src/doc.rs92
-rw-r--r--src/error.rs47
-rw-r--r--src/lib.rs529
-rw-r--r--tests/test_doc.rs44
-rw-r--r--tests/test_expr.rs201
-rw-r--r--tests/test_item.rs (renamed from tests/test.rs)247
-rw-r--r--tests/ui/case-warning.rs4
-rw-r--r--tests/ui/case-warning.stderr6
-rw-r--r--tests/ui/env-empty.rs4
-rw-r--r--tests/ui/env-empty.stderr4
-rw-r--r--tests/ui/env-non-string.rs4
-rw-r--r--tests/ui/env-non-string.stderr4
-rw-r--r--tests/ui/env-suffix.rs4
-rw-r--r--tests/ui/env-suffix.stderr4
-rw-r--r--tests/ui/env-unexpected.rs4
-rw-r--r--tests/ui/env-unexpected.stderr4
-rw-r--r--tests/ui/invalid-ident.rs4
-rw-r--r--tests/ui/invalid-ident.stderr4
-rw-r--r--tests/ui/missing-paren-on-env.rs4
-rw-r--r--tests/ui/missing-paren-on-env.stderr4
-rw-r--r--tests/ui/no-env-var.rs4
-rw-r--r--tests/ui/no-env-var.stderr4
-rw-r--r--tests/ui/no-ident-after-colon.rs4
-rw-r--r--tests/ui/no-ident-after-colon.stderr4
-rw-r--r--tests/ui/unexpected-group.rs4
-rw-r--r--tests/ui/unexpected-group.stderr4
-rw-r--r--tests/ui/unexpected-modifier.rs4
-rw-r--r--tests/ui/unexpected-modifier.stderr4
-rw-r--r--tests/ui/unexpected-punct.rs4
-rw-r--r--tests/ui/unexpected-punct.stderr4
-rw-r--r--tests/ui/unsupported-literal.rs4
-rw-r--r--tests/ui/unsupported-literal.stderr4
-rw-r--r--tests/ui/unsupported-modifier.rs4
-rw-r--r--tests/ui/unsupported-modifier.stderr4
41 files changed, 1033 insertions, 373 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 77d0b53..9a7aced 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "ca72ba450ad4859c5a7557371560a022649b1b1e"
+ "sha1": "949f5cc56255f400dfa6ffbe2a7d7e4fee7c6d92"
}
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b4476e1..4412b64 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,18 +12,21 @@ jobs:
strategy:
fail-fast: false
matrix:
- rust: [nightly, beta, stable, 1.32.0]
- include:
- - rust: 1.31.0
- rustflags: --cfg no_literal_matcher
+ rust: [nightly, beta, stable, 1.45.0]
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{matrix.rust}}
- run: cargo test
- env:
- RUSTFLAGS: ${{matrix.rustflags}}
+
+ msrv:
+ name: Rust 1.31.0
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: dtolnay/rust-toolchain@1.31.0
+ - run: cargo test --test test_item
minimal:
name: Minimal versions
@@ -33,3 +36,11 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
- run: cargo -Z minimal-versions generate-lockfile
- run: cargo check --locked
+
+ clippy:
+ name: Clippy
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: dtolnay/rust-toolchain@clippy
+ - run: cargo clippy -- -Dclippy::all
diff --git a/Android.bp b/Android.bp
index 92a3ec1..d31df24 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,17 +1,8 @@
-// This file is generated by cargo2android.py --run --dependencies --device.
+// This file is generated by cargo2android.py --run --dependencies.
-rust_library {
+rust_proc_macro {
name: "libpaste",
- host_supported: true,
crate_name: "paste",
srcs: ["src/lib.rs"],
edition: "2018",
- proc_macros: [
- "libpaste_impl",
- "libproc_macro_hack",
- ],
}
-
-// dependent_library ["feature_list"]
-// paste-impl-0.1.18
-// proc-macro-hack-0.5.16
diff --git a/Cargo.toml b/Cargo.toml
index 7c1f3ac..d4a5861 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "paste"
-version = "0.1.18"
+version = "1.0.0"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "Macros for all your token pasting needs"
readme = "README.md"
@@ -21,11 +21,9 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/dtolnay/paste"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
-[dependencies.paste-impl]
-version = "=0.1.18"
-[dependencies.proc-macro-hack]
-version = "0.5.9"
+[lib]
+proc-macro = true
[dev-dependencies.rustversion]
version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index cbcccc7..216c2f5 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "paste"
-version = "0.1.18"
+version = "1.0.0"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -8,16 +8,12 @@ description = "Macros for all your token pasting needs"
repository = "https://github.com/dtolnay/paste"
readme = "README.md"
-[dependencies]
-paste-impl = { version = "=0.1.18", path = "impl" }
-proc-macro-hack = "0.5.9"
+[lib]
+proc-macro = true
[dev-dependencies]
rustversion = "1.0"
trybuild = "1.0"
-[workspace]
-members = ["impl"]
-
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
diff --git a/METADATA b/METADATA
index 7271321..e08457b 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/dtolnay/paste"
}
- version: "0.1.18"
+ version: "1.0.0"
license_type: NOTICE
last_upgrade_date {
year: 2020
month: 7
- day: 10
+ day: 26
}
}
diff --git a/README.md b/README.md
index 25db4d3..19fa99b 100644
--- a/README.md
+++ b/README.md
@@ -17,32 +17,29 @@ including using pasted identifiers to define new items.
```toml
[dependencies]
-paste = "0.1"
+paste = "1.0"
```
-This approach works with any stable or nightly Rust compiler 1.30+.
+This approach works with any Rust compiler 1.31+.
<br>
## Pasting identifiers
-There are two entry points, `paste::expr!` for macros in expression position and
-`paste::item!` for macros in item position.
-
-Within either one, identifiers inside `[<`...`>]` are pasted together to form a
-single identifier.
+Within the `paste!` macro, identifiers inside `[<`...`>]` are pasted together to
+form a single identifier.
```rust
-// Macro in item position: at module scope or inside of an impl block.
-paste::item! {
+use paste::paste;
+
+paste! {
// Defines a const called `QRST`.
const [<Q R S T>]: &str = "success!";
}
fn main() {
- // Macro in expression position: inside a function body.
assert_eq!(
- paste::expr! { [<Q R S T>].len() },
+ paste! { [<Q R S T>].len() },
8,
);
}
@@ -50,34 +47,15 @@ fn main() {
<br>
-## More elaborate examples
-
-This program demonstrates how you may want to bundle a paste invocation inside
-of a more convenient user-facing macro of your own. Here the `routes!(A, B)`
-macro expands to a vector containing `ROUTE_A` and `ROUTE_B`.
-
-```rust
-const ROUTE_A: &str = "/a";
-const ROUTE_B: &str = "/b";
-
-macro_rules! routes {
- ($($route:ident),*) => {{
- paste::expr! {
- vec![$( [<ROUTE_ $route>] ),*]
- }
- }}
-}
-
-fn main() {
- let routes = routes!(A, B);
- assert_eq!(routes, vec!["/a", "/b"]);
-}
-```
+## More elaborate example
The next example shows a macro that generates accessor methods for some struct
-fields.
+fields. It demonstrates how you might find it useful to bundle a paste
+invocation inside of a macro\_rules macro.
```rust
+use paste::paste;
+
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define a struct. This expands to:
@@ -100,7 +78,7 @@ macro_rules! make_a_struct_and_getters {
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
- paste::item! {
+ paste! {
impl $name {
$(
pub fn [<get_ $field>](&self) -> &str {
@@ -139,6 +117,28 @@ The precise Unicode conversions are as defined by [`str::to_lowercase`] and
<br>
+## Pasting documentation strings
+
+Within the `paste!` macro, arguments to a #\[doc ...\] attribute are implicitly
+concatenated together to form a coherent documentation string.
+
+```rust
+use paste::paste;
+
+macro_rules! method_new {
+ ($ret:ident) => {
+ paste! {
+ #[doc = "Create a new `" $ret "` object."]
+ pub fn new() -> $ret { todo!() }
+ }
+ };
+}
+
+method_new!(Paste); // expands to #[doc = "Create a new `Paste` object"]
+```
+
+<br>
+
#### License
<sup>
diff --git a/src/doc.rs b/src/doc.rs
new file mode 100644
index 0000000..81d4184
--- /dev/null
+++ b/src/doc.rs
@@ -0,0 +1,92 @@
+use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
+use std::iter;
+use std::str::FromStr;
+
+pub fn is_pasted_doc(input: &TokenStream) -> bool {
+ #[derive(PartialEq)]
+ enum State {
+ Init,
+ Doc,
+ Equal,
+ First,
+ Rest,
+ }
+
+ let mut state = State::Init;
+ for tt in input.clone() {
+ state = match (state, &tt) {
+ (State::Init, TokenTree::Ident(ident)) if ident.to_string() == "doc" => State::Doc,
+ (State::Doc, TokenTree::Punct(punct)) if punct.as_char() == '=' => State::Equal,
+ (State::Equal, tt) if is_stringlike(tt) => State::First,
+ (State::First, tt) | (State::Rest, tt) if is_stringlike(tt) => State::Rest,
+ _ => return false,
+ };
+ }
+
+ state == State::Rest
+}
+
+pub fn do_paste_doc(attr: &TokenStream, span: Span) -> TokenStream {
+ let mut expanded = TokenStream::new();
+ let mut tokens = attr.clone().into_iter();
+ expanded.extend(tokens.by_ref().take(2)); // `doc =`
+
+ let mut lit = String::new();
+ lit.push('"');
+ for token in tokens {
+ lit += &escaped_string_value(&token).unwrap();
+ }
+ lit.push('"');
+
+ let mut lit = TokenStream::from_str(&lit)
+ .unwrap()
+ .into_iter()
+ .next()
+ .unwrap();
+ lit.set_span(span);
+ expanded.extend(iter::once(lit));
+ expanded
+}
+
+fn is_stringlike(token: &TokenTree) -> bool {
+ escaped_string_value(token).is_some()
+}
+
+fn escaped_string_value(token: &TokenTree) -> Option<String> {
+ match token {
+ TokenTree::Ident(ident) => Some(ident.to_string()),
+ TokenTree::Literal(literal) => {
+ let mut repr = literal.to_string();
+ if repr.starts_with('b') || repr.starts_with('\'') {
+ None
+ } else if repr.starts_with('"') {
+ repr.truncate(repr.len() - 1);
+ repr.remove(0);
+ Some(repr)
+ } else if repr.starts_with('r') {
+ let begin = repr.find('"').unwrap() + 1;
+ let end = repr.rfind('"').unwrap();
+ let mut escaped = String::new();
+ for ch in repr[begin..end].chars() {
+ escaped.extend(ch.escape_default());
+ }
+ Some(escaped)
+ } else {
+ Some(repr)
+ }
+ }
+ TokenTree::Group(group) => {
+ if group.delimiter() != Delimiter::None {
+ return None;
+ }
+ let mut inner = group.stream().into_iter();
+ let first = inner.next()?;
+ if inner.next().is_none() {
+ escaped_string_value(&first)
+ } else {
+ None
+ }
+ }
+ TokenTree::Punct(_) => None,
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..7c5badb
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,47 @@
+use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub struct Error {
+ begin: Span,
+ end: Span,
+ msg: String,
+}
+
+impl Error {
+ pub fn new(span: Span, msg: &str) -> Self {
+ Self::new2(span, span, msg)
+ }
+
+ pub fn new2(begin: Span, end: Span, msg: &str) -> Self {
+ Error {
+ begin,
+ end,
+ msg: msg.to_owned(),
+ }
+ }
+
+ pub fn to_compile_error(&self) -> TokenStream {
+ // compile_error! { $msg }
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("compile_error", self.begin)),
+ TokenTree::Punct({
+ let mut punct = Punct::new('!', Spacing::Alone);
+ punct.set_span(self.begin);
+ punct
+ }),
+ TokenTree::Group({
+ let mut group = Group::new(Delimiter::Brace, {
+ TokenStream::from_iter(vec![TokenTree::Literal({
+ let mut string = Literal::string(&self.msg);
+ string.set_span(self.end);
+ string
+ })])
+ });
+ group.set_span(self.end);
+ group
+ }),
+ ])
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index ab5806a..8fc755b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,29 +15,26 @@
//! This crate provides a flexible way to paste together identifiers in a macro,
//! including using pasted identifiers to define new items.
//!
-//! This approach works with any stable or nightly Rust compiler 1.30+.
+//! This approach works with any Rust compiler 1.31+.
//!
//! <br>
//!
//! # Pasting identifiers
//!
-//! There are two entry points, `paste::expr!` for macros in expression position and
-//! `paste::item!` for macros in item position.
-//!
-//! Within either one, identifiers inside `[<`...`>]` are pasted together to form a
-//! single identifier.
+//! Within the `paste!` macro, identifiers inside `[<`...`>]` are pasted
+//! together to form a single identifier.
//!
//! ```
-//! // Macro in item position: at module scope or inside of an impl block.
-//! paste::item! {
+//! use paste::paste;
+//!
+//! paste! {
//! // Defines a const called `QRST`.
//! const [<Q R S T>]: &str = "success!";
//! }
//!
//! fn main() {
-//! // Macro in expression position: inside a function body.
//! assert_eq!(
-//! paste::expr! { [<Q R S T>].len() },
+//! paste! { [<Q R S T>].len() },
//! 8,
//! );
//! }
@@ -45,34 +42,15 @@
//!
//! <br><br>
//!
-//! # More elaborate examples
+//! # More elaborate example
//!
-//! This program demonstrates how you may want to bundle a paste invocation inside
-//! of a more convenient user-facing macro of your own. Here the `routes!(A, B)`
-//! macro expands to a vector containing `ROUTE_A` and `ROUTE_B`.
+//! The next example shows a macro that generates accessor methods for some
+//! struct fields. It demonstrates how you might find it useful to bundle a
+//! paste invocation inside of a macro\_rules macro.
//!
//! ```
-//! const ROUTE_A: &str = "/a";
-//! const ROUTE_B: &str = "/b";
-//!
-//! macro_rules! routes {
-//! ($($route:ident),*) => {{
-//! paste::expr! {
-//! vec![$( [<ROUTE_ $route>] ),*]
-//! }
-//! }}
-//! }
+//! use paste::paste;
//!
-//! fn main() {
-//! let routes = routes!(A, B);
-//! assert_eq!(routes, vec!["/a", "/b"]);
-//! }
-//! ```
-//!
-//! The next example shows a macro that generates accessor methods for some struct
-//! fields.
-//!
-//! ```
//! macro_rules! make_a_struct_and_getters {
//! ($name:ident { $($field:ident),* }) => {
//! // Define a struct. This expands to:
@@ -95,7 +73,7 @@
//! // pub fn get_b(&self) -> &str { &self.b }
//! // pub fn get_c(&self) -> &str { &self.c }
//! // }
-//! paste::item! {
+//! paste! {
//! impl $name {
//! $(
//! pub fn [<get_ $field>](&self) -> &str {
@@ -134,28 +112,475 @@
//!
//! [`str::to_lowercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
//! [`str::to_uppercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_uppercase
+//!
+//! <br>
+//!
+//! # Pasting documentation strings
+//!
+//! Within the `paste!` macro, arguments to a #\[doc ...\] attribute are
+//! implicitly concatenated together to form a coherent documentation string.
+//!
+//! ```
+//! use paste::paste;
+//!
+//! macro_rules! method_new {
+//! ($ret:ident) => {
+//! paste! {
+//! #[doc = "Create a new `" $ret "` object."]
+//! pub fn new() -> $ret { todo!() }
+//! }
+//! };
+//! }
+//!
+//! # struct Paste;
+//! method_new!(Paste); // expands to #[doc = "Create a new `Paste` object"]
+//! ```
-#![no_std]
+#![allow(clippy::needless_doctest_main)]
-// ANDROID: Use std to allow building as a dylib.
-extern crate std;
+extern crate proc_macro;
-use proc_macro_hack::proc_macro_hack;
+mod doc;
+mod error;
-/// Paste identifiers within a macro invocation that expands to an expression.
-#[proc_macro_hack]
-pub use paste_impl::expr;
+use crate::doc::{do_paste_doc, is_pasted_doc};
+use crate::error::{Error, Result};
+use proc_macro::{
+ token_stream, Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree,
+};
+use std::iter::{self, FromIterator, Peekable};
+use std::panic;
-/// Paste identifiers within a macro invocation that expands to one or more
-/// items.
-///
-/// An item is like a struct definition, function, impl block, or anything else
-/// that can appear at the top level of a module scope.
-pub use paste_impl::item;
+#[proc_macro]
+pub fn paste(input: TokenStream) -> TokenStream {
+ let mut contains_paste = false;
+ match expand(input, &mut contains_paste) {
+ Ok(expanded) => expanded,
+ Err(err) => err.to_compile_error(),
+ }
+}
-/// Paste identifiers within a macro invocation that expands to one or more
-/// macro_rules macros or items containing macros.
-pub use paste_impl::item_with_macros;
+#[doc(hidden)]
+#[proc_macro]
+pub fn item(input: TokenStream) -> TokenStream {
+ paste(input)
+}
#[doc(hidden)]
-pub use paste_impl::EnumHack;
+#[proc_macro]
+pub fn expr(input: TokenStream) -> TokenStream {
+ paste(input)
+}
+
+fn expand(input: TokenStream, contains_paste: &mut bool) -> Result<TokenStream> {
+ let mut expanded = TokenStream::new();
+ let mut lookbehind = Lookbehind::Other;
+ let mut prev_none_group = None::<Group>;
+ let mut tokens = input.into_iter().peekable();
+ loop {
+ let token = tokens.next();
+ if let Some(group) = prev_none_group.take() {
+ if match (&token, tokens.peek()) {
+ (Some(TokenTree::Punct(fst)), Some(TokenTree::Punct(snd))) => {
+ fst.as_char() == ':' && snd.as_char() == ':' && fst.spacing() == Spacing::Joint
+ }
+ _ => false,
+ } {
+ expanded.extend(group.stream());
+ *contains_paste = true;
+ } else {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ }
+ }
+ match token {
+ Some(TokenTree::Group(group)) => {
+ let delimiter = group.delimiter();
+ let content = group.stream();
+ let span = group.span();
+ if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
+ let segments = parse_bracket_as_segments(content, span)?;
+ let pasted = paste_segments(span, &segments)?;
+ expanded.extend(pasted);
+ *contains_paste = true;
+ } else if delimiter == Delimiter::None && is_flat_group(&content) {
+ expanded.extend(content);
+ *contains_paste = true;
+ } else if delimiter == Delimiter::Bracket
+ && (lookbehind == Lookbehind::Pound || lookbehind == Lookbehind::PoundBang)
+ && is_pasted_doc(&content)
+ {
+ let pasted = do_paste_doc(&content, span);
+ let mut group = Group::new(delimiter, pasted);
+ group.set_span(span);
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ *contains_paste = true;
+ } else {
+ let mut group_contains_paste = false;
+ let nested = expand(content, &mut group_contains_paste)?;
+ let group = if group_contains_paste {
+ let mut group = Group::new(delimiter, nested);
+ group.set_span(span);
+ *contains_paste = true;
+ group
+ } else {
+ group.clone()
+ };
+ if delimiter != Delimiter::None {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ } else if lookbehind == Lookbehind::DoubleColon {
+ expanded.extend(group.stream());
+ *contains_paste = true;
+ } else {
+ prev_none_group = Some(group);
+ }
+ }
+ lookbehind = Lookbehind::Other;
+ }
+ Some(TokenTree::Punct(punct)) => {
+ lookbehind = match punct.as_char() {
+ ':' if lookbehind == Lookbehind::JointColon => Lookbehind::DoubleColon,
+ ':' if punct.spacing() == Spacing::Joint => Lookbehind::JointColon,
+ '#' => Lookbehind::Pound,
+ '!' if lookbehind == Lookbehind::Pound => Lookbehind::PoundBang,
+ _ => Lookbehind::Other,
+ };
+ expanded.extend(iter::once(TokenTree::Punct(punct)));
+ }
+ Some(other) => {
+ lookbehind = Lookbehind::Other;
+ expanded.extend(iter::once(other));
+ }
+ None => return Ok(expanded),
+ }
+ }
+}
+
+#[derive(PartialEq)]
+enum Lookbehind {
+ JointColon,
+ DoubleColon,
+ Pound,
+ PoundBang,
+ Other,
+}
+
+// https://github.com/dtolnay/paste/issues/26
+fn is_flat_group(input: &TokenStream) -> bool {
+ #[derive(PartialEq)]
+ enum State {
+ Init,
+ Ident,
+ Literal,
+ Apostrophe,
+ Lifetime,
+ Colon1,
+ Colon2,
+ }
+
+ let mut state = State::Init;
+ for tt in input.clone() {
+ state = match (state, &tt) {
+ (State::Init, TokenTree::Ident(_)) => State::Ident,
+ (State::Init, TokenTree::Literal(_)) => State::Literal,
+ (State::Init, TokenTree::Punct(punct)) if punct.as_char() == '\'' => State::Apostrophe,
+ (State::Apostrophe, TokenTree::Ident(_)) => State::Lifetime,
+ (State::Ident, TokenTree::Punct(punct))
+ if punct.as_char() == ':' && punct.spacing() == Spacing::Joint =>
+ {
+ State::Colon1
+ }
+ (State::Colon1, TokenTree::Punct(punct))
+ if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
+ {
+ State::Colon2
+ }
+ (State::Colon2, TokenTree::Ident(_)) => State::Ident,
+ _ => return false,
+ };
+ }
+
+ state == State::Ident || state == State::Literal || state == State::Lifetime
+}
+
+struct LitStr {
+ value: String,
+ span: Span,
+}
+
+struct Colon {
+ span: Span,
+}
+
+enum Segment {
+ String(String),
+ Apostrophe(Span),
+ Env(LitStr),
+ Modifier(Colon, Ident),
+}
+
+fn is_paste_operation(input: &TokenStream) -> bool {
+ let mut tokens = input.clone().into_iter();
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ _ => return false,
+ }
+
+ let mut has_token = false;
+ loop {
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {
+ return has_token && tokens.next().is_none();
+ }
+ Some(_) => has_token = true,
+ None => return false,
+ }
+ }
+}
+
+fn parse_bracket_as_segments(input: TokenStream, scope: Span) -> Result<Vec<Segment>> {
+ let mut tokens = input.into_iter().peekable();
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `<`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ let segments = parse_segments(&mut tokens, scope)?;
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `>`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ match tokens.next() {
+ Some(unexpected) => Err(Error::new(
+ unexpected.span(),
+ "unexpected input, expected `[< ... >]`",
+ )),
+ None => Ok(segments),
+ }
+}
+
+fn parse_segments(
+ tokens: &mut Peekable<token_stream::IntoIter>,
+ scope: Span,
+) -> Result<Vec<Segment>> {
+ let mut segments = Vec::new();
+ while match tokens.peek() {
+ None => false,
+ Some(TokenTree::Punct(punct)) => punct.as_char() != '>',
+ Some(_) => true,
+ } {
+ match tokens.next().unwrap() {
+ TokenTree::Ident(ident) => {
+ let mut fragment = ident.to_string();
+ if fragment.starts_with("r#") {
+ fragment = fragment.split_off(2);
+ }
+ if fragment == "env"
+ && match tokens.peek() {
+ Some(TokenTree::Punct(punct)) => punct.as_char() == '!',
+ _ => false,
+ }
+ {
+ tokens.next().unwrap(); // `!`
+ let expect_group = tokens.next();
+ let parenthesized = match &expect_group {
+ Some(TokenTree::Group(group))
+ if group.delimiter() == Delimiter::Parenthesis =>
+ {
+ group
+ }
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `(`")),
+ None => return Err(Error::new(scope, "expected `(` after `env!`")),
+ };
+ let mut inner = parenthesized.stream().into_iter();
+ let lit = match inner.next() {
+ Some(TokenTree::Literal(lit)) => lit,
+ Some(wrong) => {
+ return Err(Error::new(wrong.span(), "expected string literal"))
+ }
+ None => {
+ return Err(Error::new2(
+ ident.span(),
+ parenthesized.span(),
+ "expected string literal as argument to env! macro",
+ ))
+ }
+ };
+ let lit_string = lit.to_string();
+ if lit_string.starts_with('"')
+ && lit_string.ends_with('"')
+ && lit_string.len() >= 2
+ {
+ // TODO: maybe handle escape sequences in the string if
+ // someone has a use case.
+ segments.push(Segment::Env(LitStr {
+ value: lit_string[1..lit_string.len() - 1].to_owned(),
+ span: lit.span(),
+ }));
+ } else {
+ return Err(Error::new(lit.span(), "expected string literal"));
+ }
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(
+ unexpected.span(),
+ "unexpected token in env! macro",
+ ));
+ }
+ } else {
+ segments.push(Segment::String(fragment));
+ }
+ }
+ TokenTree::Literal(lit) => {
+ let mut lit_string = lit.to_string();
+ if lit_string.contains(&['#', '\\', '.', '+'][..]) {
+ return Err(Error::new(lit.span(), "unsupported literal"));
+ }
+ lit_string = lit_string
+ .replace('"', "")
+ .replace('\'', "")
+ .replace('-', "_");
+ segments.push(Segment::String(lit_string));
+ }
+ TokenTree::Punct(punct) => match punct.as_char() {
+ '_' => segments.push(Segment::String("_".to_owned())),
+ '\'' => segments.push(Segment::Apostrophe(punct.span())),
+ ':' => {
+ let colon = Colon { span: punct.span() };
+ let ident = match tokens.next() {
+ Some(TokenTree::Ident(ident)) => ident,
+ wrong => {
+ let span = wrong.as_ref().map_or(scope, TokenTree::span);
+ return Err(Error::new(span, "expected identifier after `:`"));
+ }
+ };
+ segments.push(Segment::Modifier(colon, ident));
+ }
+ _ => return Err(Error::new(punct.span(), "unexpected punct")),
+ },
+ TokenTree::Group(group) => {
+ if group.delimiter() == Delimiter::None {
+ let mut inner = group.stream().into_iter().peekable();
+ let nested = parse_segments(&mut inner, group.span())?;
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(unexpected.span(), "unexpected token"));
+ }
+ segments.extend(nested);
+ } else {
+ return Err(Error::new(group.span(), "unexpected token"));
+ }
+ }
+ }
+ }
+ Ok(segments)
+}
+
+fn paste_segments(span: Span, segments: &[Segment]) -> Result<TokenStream> {
+ let mut evaluated = Vec::new();
+ let mut is_lifetime = false;
+
+ for segment in segments {
+ match segment {
+ Segment::String(segment) => {
+ evaluated.push(segment.clone());
+ }
+ Segment::Apostrophe(span) => {
+ if is_lifetime {
+ return Err(Error::new(*span, "unexpected lifetime"));
+ }
+ is_lifetime = true;
+ }
+ Segment::Env(var) => {
+ let resolved = match std::env::var(&var.value) {
+ Ok(resolved) => resolved,
+ Err(_) => {
+ return Err(Error::new(
+ var.span,
+ &format!("no such env var: {:?}", var.value),
+ ));
+ }
+ };
+ let resolved = resolved.replace('-', "_");
+ evaluated.push(resolved);
+ }
+ Segment::Modifier(colon, ident) => {
+ let last = match evaluated.pop() {
+ Some(last) => last,
+ None => {
+ return Err(Error::new2(colon.span, ident.span(), "unexpected modifier"))
+ }
+ };
+ match ident.to_string().as_str() {
+ "lower" => {
+ evaluated.push(last.to_lowercase());
+ }
+ "upper" => {
+ evaluated.push(last.to_uppercase());
+ }
+ "snake" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch.is_uppercase() && prev != '_' {
+ acc.push('_');
+ }
+ acc.push(ch);
+ prev = ch;
+ }
+ evaluated.push(acc.to_lowercase());
+ }
+ "camel" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch != '_' {
+ if prev == '_' {
+ for chu in ch.to_uppercase() {
+ acc.push(chu);
+ }
+ } else if prev.is_uppercase() {
+ for chl in ch.to_lowercase() {
+ acc.push(chl);
+ }
+ } else {
+ acc.push(ch);
+ }
+ }
+ prev = ch;
+ }
+ evaluated.push(acc);
+ }
+ _ => {
+ return Err(Error::new2(
+ colon.span,
+ ident.span(),
+ "unsupported modifier",
+ ));
+ }
+ }
+ }
+ }
+ }
+
+ let pasted = evaluated.into_iter().collect::<String>();
+ let ident = match panic::catch_unwind(|| Ident::new(&pasted, span)) {
+ Ok(ident) => TokenTree::Ident(ident),
+ Err(_) => {
+ return Err(Error::new(
+ span,
+ &format!("`{:?}` is not a valid identifier", pasted),
+ ));
+ }
+ };
+ let tokens = if is_lifetime {
+ let apostrophe = TokenTree::Punct(Punct::new('\'', Spacing::Joint));
+ vec![apostrophe, ident]
+ } else {
+ vec![ident]
+ };
+ Ok(TokenStream::from_iter(tokens))
+}
diff --git a/tests/test_doc.rs b/tests/test_doc.rs
new file mode 100644
index 0000000..96fe3a0
--- /dev/null
+++ b/tests/test_doc.rs
@@ -0,0 +1,44 @@
+use paste::paste;
+
+#[test]
+fn test_paste_doc() {
+ macro_rules! m {
+ ($ret:ident) => {
+ paste! {
+ #[doc = "Create a new [`" $ret "`] object."]
+ fn new() -> $ret { todo!() }
+ }
+ };
+ }
+
+ struct Paste;
+ m!(Paste);
+
+ let _ = new;
+}
+
+macro_rules! get_doc {
+ (#[doc = $literal:tt]) => {
+ $literal
+ };
+}
+
+#[test]
+fn test_escaping() {
+ let doc = paste! {
+ get_doc!(#[doc = "s\"" r#"r#""#])
+ };
+
+ let expected = "s\"r#\"";
+ assert_eq!(doc, expected);
+}
+
+#[test]
+fn test_literals() {
+ let doc = paste! {
+ get_doc!(#[doc = "int=" 0x1 " bool=" true " float=" 0.01])
+ };
+
+ let expected = "int=0x1 bool=true float=0.01";
+ assert_eq!(doc, expected);
+}
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
new file mode 100644
index 0000000..8186a37
--- /dev/null
+++ b/tests/test_expr.rs
@@ -0,0 +1,201 @@
+use paste::paste;
+
+#[test]
+fn test_shared_hygiene() {
+ paste! {
+ let [<a a>] = 1;
+ assert_eq!([<a a>], 1);
+ }
+}
+
+#[test]
+fn test_repeat() {
+ const ROCKET_A: &'static str = "/a";
+ const ROCKET_B: &'static str = "/b";
+
+ macro_rules! routes {
+ ($($route:ident),*) => {{
+ paste! {
+ vec![$( [<ROCKET_ $route>] ),*]
+ }
+ }}
+ }
+
+ let routes = routes!(A, B);
+ assert_eq!(routes, vec!["/a", "/b"]);
+}
+
+#[test]
+fn test_integer() {
+ const CONST0: &'static str = "const0";
+
+ let pasted = paste!([<CONST 0>]);
+ assert_eq!(pasted, CONST0);
+}
+
+#[test]
+fn test_underscore() {
+ paste! {
+ const A_B: usize = 0;
+ assert_eq!([<A _ B>], 0);
+ }
+}
+
+#[test]
+fn test_lifetime() {
+ paste! {
+ #[allow(dead_code)]
+ struct S<[<'d e>]> {
+ q: &[<'d e>] str,
+ }
+ }
+}
+
+#[test]
+fn test_keyword() {
+ paste! {
+ struct [<F move>];
+
+ let _ = Fmove;
+ }
+}
+
+#[test]
+fn test_literal_str() {
+ paste! {
+ #[allow(non_camel_case_types)]
+ struct [<Foo "Bar-Baz">];
+
+ let _ = FooBar_Baz;
+ }
+}
+
+#[test]
+fn test_env_literal() {
+ paste! {
+ struct [<Lib env bar>];
+
+ let _ = Libenvbar;
+ }
+}
+
+#[test]
+fn test_env_present() {
+ paste! {
+ struct [<Lib env!("CARGO_PKG_NAME")>];
+
+ let _ = Libpaste;
+ }
+}
+
+#[test]
+fn test_raw_identifier() {
+ paste! {
+ struct [<F r#move>];
+
+ let _ = Fmove;
+ }
+}
+
+#[test]
+fn test_false_start() {
+ trait Trait {
+ fn f() -> usize;
+ }
+
+ struct S;
+
+ impl Trait for S {
+ fn f() -> usize {
+ 0
+ }
+ }
+
+ paste! {
+ let x = [<S as Trait>::f()];
+ assert_eq!(x[0], 0);
+ }
+}
+
+#[test]
+fn test_local_variable() {
+ let yy = 0;
+
+ paste! {
+ assert_eq!([<y y>], 0);
+ }
+}
+
+#[test]
+fn test_empty() {
+ paste! {
+ assert_eq!(stringify!([<y y>]), "yy");
+ assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]");
+ }
+}
+
+#[test]
+fn test_env_to_lower() {
+ paste! {
+ struct [<Lib env!("CARGO_PKG_NAME"):lower>];
+
+ let _ = Libpaste;
+ }
+}
+
+#[test]
+fn test_env_to_upper() {
+ paste! {
+ const [<LIB env!("CARGO_PKG_NAME"):upper>]: &str = "libpaste";
+
+ let _ = LIBPASTE;
+ }
+}
+
+#[test]
+fn test_env_to_snake() {
+ paste! {
+ const [<LIB env!("CARGO_PKG_NAME"):snake:upper>]: &str = "libpaste";
+
+ let _ = LIBPASTE;
+ }
+}
+
+#[test]
+fn test_env_to_camel() {
+ paste! {
+ #[allow(non_upper_case_globals)]
+ const [<LIB env!("CARGO_PKG_NAME"):camel>]: &str = "libpaste";
+
+ let _ = LIBPaste;
+ }
+}
+
+mod test_x86_feature_literal {
+ // work around https://github.com/rust-lang/rust/issues/72726
+
+ use paste::paste;
+
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ macro_rules! my_is_x86_feature_detected {
+ ($feat:literal) => {
+ paste! {
+ #[test]
+ fn test() {
+ let _ = is_x86_feature_detected!($feat);
+ }
+ }
+ };
+ }
+
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+ macro_rules! my_is_x86_feature_detected {
+ ($feat:literal) => {
+ #[ignore]
+ #[test]
+ fn test() {}
+ };
+ }
+
+ my_is_x86_feature_detected!("mmx");
+}
diff --git a/tests/test.rs b/tests/test_item.rs
index e6dcdfa..86c98a9 100644
--- a/tests/test.rs
+++ b/tests/test_item.rs
@@ -1,7 +1,9 @@
mod test_basic {
+ use paste::paste;
+
struct Struct;
- paste::item! {
+ paste! {
impl Struct {
fn [<a b c>]() {}
}
@@ -14,10 +16,12 @@ mod test_basic {
}
mod test_in_impl {
+ use paste::paste;
+
struct Struct;
impl Struct {
- paste::item! {
+ paste! {
fn [<a b c>]() {}
}
}
@@ -28,143 +32,12 @@ mod test_in_impl {
}
}
-#[test]
-fn test_shared_hygiene() {
- paste::expr! {
- let [<a a>] = 1;
- assert_eq!([<a a>], 1);
- }
-}
-
-#[test]
-fn test_repeat() {
- const ROCKET_A: &'static str = "/a";
- const ROCKET_B: &'static str = "/b";
-
- macro_rules! routes {
- ($($route:ident),*) => {{
- paste::expr! {
- vec![$( [<ROCKET_ $route>] ),*]
- }
- }}
- }
-
- let routes = routes!(A, B);
- assert_eq!(routes, vec!["/a", "/b"]);
-}
-
-#[test]
-fn test_integer() {
- const CONST0: &'static str = "const0";
-
- let pasted = paste::expr!([<CONST 0>]);
- assert_eq!(pasted, CONST0);
-}
-
-#[test]
-fn test_underscore() {
- paste::expr! {
- const A_B: usize = 0;
- assert_eq!([<A _ B>], 0);
- }
-}
-
-#[test]
-fn test_lifetime() {
- paste::expr! {
- #[allow(dead_code)]
- struct S<[<'d e>]> {
- q: &[<'d e>] str,
- }
- }
-}
-
-#[test]
-fn test_keyword() {
- paste::expr! {
- struct [<F move>];
-
- let _ = Fmove;
- }
-}
-
-#[test]
-fn test_literal_str() {
- paste::expr! {
- struct [<Foo "Bar-Baz">];
-
- let _ = FooBar_Baz;
- }
-}
-
-#[test]
-fn test_env_literal() {
- paste::expr! {
- struct [<Lib env bar>];
-
- let _ = Libenvbar;
- }
-}
-
-#[test]
-fn test_env_present() {
- paste::expr! {
- struct [<Lib env!("CARGO_PKG_NAME")>];
-
- let _ = Libpaste;
- }
-}
-
-#[test]
-fn test_raw_identifier() {
- paste::expr! {
- struct [<F r#move>];
-
- let _ = Fmove;
- }
-}
-
-#[test]
-fn test_false_start() {
- trait Trait {
- fn f() -> usize;
- }
-
- struct S;
-
- impl Trait for S {
- fn f() -> usize {
- 0
- }
- }
-
- paste::expr! {
- let x = [<S as Trait>::f()];
- assert_eq!(x[0], 0);
- }
-}
-
-#[test]
-fn test_local_variable() {
- let yy = 0;
-
- paste::expr! {
- assert_eq!([<y y>], 0);
- }
-}
-
-#[test]
-fn test_empty() {
- paste::expr! {
- assert_eq!(stringify!([<y y>]), "yy");
- assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]");
- }
-}
-
mod test_none_delimited_single_ident {
+ use paste::paste;
+
macro_rules! m {
($id:ident) => {
- paste::item! {
+ paste! {
fn f() -> &'static str {
stringify!($id)
}
@@ -181,9 +54,11 @@ mod test_none_delimited_single_ident {
}
mod test_none_delimited_single_lifetime {
+ use paste::paste;
+
macro_rules! m {
($life:lifetime) => {
- paste::item! {
+ paste! {
pub struct S;
impl<$life> S {
fn f() {}
@@ -201,9 +76,11 @@ mod test_none_delimited_single_lifetime {
}
mod test_to_lower {
+ use paste::paste;
+
macro_rules! m {
($id:ident) => {
- paste::item! {
+ paste! {
fn [<my_ $id:lower _here>](_arg: u8) -> &'static str {
stringify!([<$id:lower>])
}
@@ -219,19 +96,12 @@ mod test_to_lower {
}
}
-#[test]
-fn test_env_to_lower() {
- paste::expr! {
- struct [<Lib env!("CARGO_PKG_NAME"):lower>];
-
- let _ = Libpaste;
- }
-}
-
mod test_to_upper {
+ use paste::paste;
+
macro_rules! m {
($id:ident) => {
- paste::item! {
+ paste! {
const [<MY_ $id:upper _HERE>]: &str = stringify!([<$id:upper>]);
}
};
@@ -245,19 +115,12 @@ mod test_to_upper {
}
}
-#[test]
-fn test_env_to_upper() {
- paste::expr! {
- const [<LIB env!("CARGO_PKG_NAME"):upper>]: &str = "libpaste";
-
- let _ = LIBPASTE;
- }
-}
-
mod test_to_snake {
+ use paste::paste;
+
macro_rules! m {
($id:ident) => {
- paste::item! {
+ paste! {
const DEFAULT_SNAKE: &str = stringify!([<$id:snake>]);
const LOWER_SNAKE: &str = stringify!([<$id:snake:lower>]);
const UPPER_SNAKE: &str = stringify!([<$id:snake:upper>]);
@@ -275,19 +138,12 @@ mod test_to_snake {
}
}
-#[test]
-fn test_env_to_snake() {
- paste::expr! {
- const [<LIB env!("CARGO_PKG_NAME"):snake:upper>]: &str = "libpaste";
-
- let _ = LIBPASTE;
- }
-}
-
mod test_to_camel {
+ use paste::paste;
+
macro_rules! m {
($id:ident) => {
- paste::item! {
+ paste! {
const DEFAULT_CAMEL: &str = stringify!([<$id:camel>]);
const LOWER_CAMEL: &str = stringify!([<$id:camel:lower>]);
const UPPER_CAMEL: &str = stringify!([<$id:camel:upper>]);
@@ -305,21 +161,14 @@ mod test_to_camel {
}
}
-#[test]
-fn test_env_to_camel() {
- paste::expr! {
- const [<LIB env!("CARGO_PKG_NAME"):camel>]: &str = "libpaste";
-
- let _ = LIBPaste;
- }
-}
-
mod test_doc_expr {
// https://github.com/dtolnay/paste/issues/29
+ use paste::paste;
+
macro_rules! doc_expr {
($doc:expr) => {
- paste::item! {
+ paste! {
#[doc = $doc]
pub struct S;
}
@@ -337,6 +186,8 @@ mod test_doc_expr {
mod test_type_in_path {
// https://github.com/dtolnay/paste/issues/31
+ use paste::paste;
+
mod keys {
#[derive(Default)]
pub struct Mib<T = ()>(std::marker::PhantomData<T>);
@@ -344,7 +195,7 @@ mod test_type_in_path {
macro_rules! types {
($mib:ty) => {
- paste::item! {
+ paste! {
#[derive(Default)]
pub struct S(pub keys::$mib);
}
@@ -353,7 +204,7 @@ mod test_type_in_path {
macro_rules! write {
($fn:ident, $field:ty) => {
- paste::item! {
+ paste! {
pub fn $fn() -> $field {
$field::default()
}
@@ -376,11 +227,13 @@ mod test_type_in_path {
mod test_type_in_fn_arg {
// https://github.com/dtolnay/paste/issues/38
+ use paste::paste;
+
fn _jit_address(_node: ()) {}
macro_rules! jit_reexport {
($fn:ident, $arg:ident : $typ:ty) => {
- paste::item! {
+ paste! {
pub fn $fn($arg: $typ) {
[<_jit_ $fn>]($arg);
}
@@ -399,9 +252,11 @@ mod test_type_in_fn_arg {
mod test_pat_in_expr_position {
// https://github.com/xiph/rav1e/pull/2324/files
+ use paste::paste;
+
macro_rules! rav1e_bad {
($e:pat) => {
- paste::item! {
+ paste! {
#[test]
fn test() {
let _ = $e;
@@ -412,31 +267,3 @@ mod test_pat_in_expr_position {
rav1e_bad!(std::fmt::Error);
}
-
-#[cfg(not(no_literal_matcher))]
-mod test_x86_feature_literal {
- // work around https://github.com/rust-lang/rust/issues/72726
-
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
- macro_rules! my_is_x86_feature_detected {
- ($feat:literal) => {
- paste::item! {
- #[test]
- fn test() {
- let _ = is_x86_feature_detected!($feat);
- }
- }
- };
- }
-
- #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
- macro_rules! my_is_x86_feature_detected {
- ($feat:literal) => {
- #[ignore]
- #[test]
- fn test() {}
- };
- }
-
- my_is_x86_feature_detected!("mmx");
-}
diff --git a/tests/ui/case-warning.rs b/tests/ui/case-warning.rs
index 4b41ab6..fdea4d6 100644
--- a/tests/ui/case-warning.rs
+++ b/tests/ui/case-warning.rs
@@ -1,8 +1,10 @@
#![deny(warnings)]
+use paste::paste;
+
macro_rules! m {
($i:ident) => {
- paste::item! {
+ paste! {
pub fn [<foo $i>]() {}
}
};
diff --git a/tests/ui/case-warning.stderr b/tests/ui/case-warning.stderr
index 5bf37a0..d140c19 100644
--- a/tests/ui/case-warning.stderr
+++ b/tests/ui/case-warning.stderr
@@ -1,10 +1,10 @@
error: function `fooBar` should have a snake case name
- --> $DIR/case-warning.rs:6:20
+ --> $DIR/case-warning.rs:8:20
|
-6 | pub fn [<foo $i>]() {}
+8 | pub fn [<foo $i>]() {}
| ^^^^^^^^^^ help: convert the identifier to snake case: `foo_bar`
...
-11 | m!(Bar);
+13 | m!(Bar);
| -------- in this macro invocation
|
note: the lint level is defined here
diff --git a/tests/ui/env-empty.rs b/tests/ui/env-empty.rs
index d1682f5..1e9f2d0 100644
--- a/tests/ui/env-empty.rs
+++ b/tests/ui/env-empty.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<env!()>]() {}
}
diff --git a/tests/ui/env-empty.stderr b/tests/ui/env-empty.stderr
index ef8f362..3e4d4da 100644
--- a/tests/ui/env-empty.stderr
+++ b/tests/ui/env-empty.stderr
@@ -1,5 +1,5 @@
error: expected string literal as argument to env! macro
- --> $DIR/env-empty.rs:2:10
+ --> $DIR/env-empty.rs:4:10
|
-2 | fn [<env!()>]() {}
+4 | fn [<env!()>]() {}
| ^^^^^^
diff --git a/tests/ui/env-non-string.rs b/tests/ui/env-non-string.rs
index d35a445..55255ef 100644
--- a/tests/ui/env-non-string.rs
+++ b/tests/ui/env-non-string.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<env!(1.31)>]() {}
}
diff --git a/tests/ui/env-non-string.stderr b/tests/ui/env-non-string.stderr
index 007ebcf..7988800 100644
--- a/tests/ui/env-non-string.stderr
+++ b/tests/ui/env-non-string.stderr
@@ -1,5 +1,5 @@
error: expected string literal
- --> $DIR/env-non-string.rs:2:15
+ --> $DIR/env-non-string.rs:4:15
|
-2 | fn [<env!(1.31)>]() {}
+4 | fn [<env!(1.31)>]() {}
| ^^^^
diff --git a/tests/ui/env-suffix.rs b/tests/ui/env-suffix.rs
index cd9e15e..b5c60af 100644
--- a/tests/ui/env-suffix.rs
+++ b/tests/ui/env-suffix.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<env!("VAR"suffix)>]() {}
}
diff --git a/tests/ui/env-suffix.stderr b/tests/ui/env-suffix.stderr
index c696fa1..82fad8b 100644
--- a/tests/ui/env-suffix.stderr
+++ b/tests/ui/env-suffix.stderr
@@ -1,5 +1,5 @@
error: expected string literal
- --> $DIR/env-suffix.rs:2:15
+ --> $DIR/env-suffix.rs:4:15
|
-2 | fn [<env!("VAR"suffix)>]() {}
+4 | fn [<env!("VAR"suffix)>]() {}
| ^^^^^^^^^^^
diff --git a/tests/ui/env-unexpected.rs b/tests/ui/env-unexpected.rs
index 1a34ae8..39cb770 100644
--- a/tests/ui/env-unexpected.rs
+++ b/tests/ui/env-unexpected.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<env!("VAR" "VAR")>]() {}
}
diff --git a/tests/ui/env-unexpected.stderr b/tests/ui/env-unexpected.stderr
index 63095de..01bf722 100644
--- a/tests/ui/env-unexpected.stderr
+++ b/tests/ui/env-unexpected.stderr
@@ -1,5 +1,5 @@
error: unexpected token in env! macro
- --> $DIR/env-unexpected.rs:2:21
+ --> $DIR/env-unexpected.rs:4:21
|
-2 | fn [<env!("VAR" "VAR")>]() {}
+4 | fn [<env!("VAR" "VAR")>]() {}
| ^^^^^
diff --git a/tests/ui/invalid-ident.rs b/tests/ui/invalid-ident.rs
index 529896d..d566e65 100644
--- a/tests/ui/invalid-ident.rs
+++ b/tests/ui/invalid-ident.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<0 f>]() {}
}
diff --git a/tests/ui/invalid-ident.stderr b/tests/ui/invalid-ident.stderr
index bec43c4..2c70cc8 100644
--- a/tests/ui/invalid-ident.stderr
+++ b/tests/ui/invalid-ident.stderr
@@ -1,5 +1,5 @@
error: `"0f"` is not a valid identifier
- --> $DIR/invalid-ident.rs:2:8
+ --> $DIR/invalid-ident.rs:4:8
|
-2 | fn [<0 f>]() {}
+4 | fn [<0 f>]() {}
| ^^^^^^^
diff --git a/tests/ui/missing-paren-on-env.rs b/tests/ui/missing-paren-on-env.rs
index b8a51b0..44fefbd 100644
--- a/tests/ui/missing-paren-on-env.rs
+++ b/tests/ui/missing-paren-on-env.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<env! huh>]() {}
}
diff --git a/tests/ui/missing-paren-on-env.stderr b/tests/ui/missing-paren-on-env.stderr
index 6e99bea..64f0391 100644
--- a/tests/ui/missing-paren-on-env.stderr
+++ b/tests/ui/missing-paren-on-env.stderr
@@ -1,5 +1,5 @@
error: expected `(`
- --> $DIR/missing-paren-on-env.rs:2:15
+ --> $DIR/missing-paren-on-env.rs:4:15
|
-2 | fn [<env! huh>]() {}
+4 | fn [<env! huh>]() {}
| ^^^
diff --git a/tests/ui/no-env-var.rs b/tests/ui/no-env-var.rs
index 87aad35..c6d8c3d 100644
--- a/tests/ui/no-env-var.rs
+++ b/tests/ui/no-env-var.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<a env!("PASTE_UNKNOWN") b>]() {}
}
diff --git a/tests/ui/no-env-var.stderr b/tests/ui/no-env-var.stderr
index e79d148..9255a8f 100644
--- a/tests/ui/no-env-var.stderr
+++ b/tests/ui/no-env-var.stderr
@@ -1,5 +1,5 @@
error: no such env var: "PASTE_UNKNOWN"
- --> $DIR/no-env-var.rs:2:17
+ --> $DIR/no-env-var.rs:4:17
|
-2 | fn [<a env!("PASTE_UNKNOWN") b>]() {}
+4 | fn [<a env!("PASTE_UNKNOWN") b>]() {}
| ^^^^^^^^^^^^^^^
diff --git a/tests/ui/no-ident-after-colon.rs b/tests/ui/no-ident-after-colon.rs
index 83a25d3..50b3b0d 100644
--- a/tests/ui/no-ident-after-colon.rs
+++ b/tests/ui/no-ident-after-colon.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<name:0>]() {}
}
diff --git a/tests/ui/no-ident-after-colon.stderr b/tests/ui/no-ident-after-colon.stderr
index 2f36bac..928316f 100644
--- a/tests/ui/no-ident-after-colon.stderr
+++ b/tests/ui/no-ident-after-colon.stderr
@@ -1,5 +1,5 @@
error: expected identifier after `:`
- --> $DIR/no-ident-after-colon.rs:2:15
+ --> $DIR/no-ident-after-colon.rs:4:15
|
-2 | fn [<name:0>]() {}
+4 | fn [<name:0>]() {}
| ^
diff --git a/tests/ui/unexpected-group.rs b/tests/ui/unexpected-group.rs
index 9fed522..63ee516 100644
--- a/tests/ui/unexpected-group.rs
+++ b/tests/ui/unexpected-group.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<a {} b>]() {}
}
diff --git a/tests/ui/unexpected-group.stderr b/tests/ui/unexpected-group.stderr
index c5d4a82..4d05139 100644
--- a/tests/ui/unexpected-group.stderr
+++ b/tests/ui/unexpected-group.stderr
@@ -1,5 +1,5 @@
error: unexpected token
- --> $DIR/unexpected-group.rs:2:12
+ --> $DIR/unexpected-group.rs:4:12
|
-2 | fn [<a {} b>]() {}
+4 | fn [<a {} b>]() {}
| ^^
diff --git a/tests/ui/unexpected-modifier.rs b/tests/ui/unexpected-modifier.rs
index 00a1e99..99fe68f 100644
--- a/tests/ui/unexpected-modifier.rs
+++ b/tests/ui/unexpected-modifier.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<:lower x>]() {}
}
diff --git a/tests/ui/unexpected-modifier.stderr b/tests/ui/unexpected-modifier.stderr
index 4a518b8..513835c 100644
--- a/tests/ui/unexpected-modifier.stderr
+++ b/tests/ui/unexpected-modifier.stderr
@@ -1,5 +1,5 @@
error: unexpected modifier
- --> $DIR/unexpected-modifier.rs:2:10
+ --> $DIR/unexpected-modifier.rs:4:10
|
-2 | fn [<:lower x>]() {}
+4 | fn [<:lower x>]() {}
| ^^^^^^
diff --git a/tests/ui/unexpected-punct.rs b/tests/ui/unexpected-punct.rs
index b2313b5..d0edb92 100644
--- a/tests/ui/unexpected-punct.rs
+++ b/tests/ui/unexpected-punct.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<a + b>]() {}
}
diff --git a/tests/ui/unexpected-punct.stderr b/tests/ui/unexpected-punct.stderr
index 9564310..456d172 100644
--- a/tests/ui/unexpected-punct.stderr
+++ b/tests/ui/unexpected-punct.stderr
@@ -1,5 +1,5 @@
error: unexpected punct
- --> $DIR/unexpected-punct.rs:2:12
+ --> $DIR/unexpected-punct.rs:4:12
|
-2 | fn [<a + b>]() {}
+4 | fn [<a + b>]() {}
| ^
diff --git a/tests/ui/unsupported-literal.rs b/tests/ui/unsupported-literal.rs
index d3768bd..6538971 100644
--- a/tests/ui/unsupported-literal.rs
+++ b/tests/ui/unsupported-literal.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<1e+100>]() {}
}
diff --git a/tests/ui/unsupported-literal.stderr b/tests/ui/unsupported-literal.stderr
index 30fe5b4..0167999 100644
--- a/tests/ui/unsupported-literal.stderr
+++ b/tests/ui/unsupported-literal.stderr
@@ -1,5 +1,5 @@
error: unsupported literal
- --> $DIR/unsupported-literal.rs:2:10
+ --> $DIR/unsupported-literal.rs:4:10
|
-2 | fn [<1e+100>]() {}
+4 | fn [<1e+100>]() {}
| ^^^^^^
diff --git a/tests/ui/unsupported-modifier.rs b/tests/ui/unsupported-modifier.rs
index 5ab049f..a65b36a 100644
--- a/tests/ui/unsupported-modifier.rs
+++ b/tests/ui/unsupported-modifier.rs
@@ -1,4 +1,6 @@
-paste::item! {
+use paste::paste;
+
+paste! {
fn [<a:pillow>]() {}
}
diff --git a/tests/ui/unsupported-modifier.stderr b/tests/ui/unsupported-modifier.stderr
index ffbc742..bfb9697 100644
--- a/tests/ui/unsupported-modifier.stderr
+++ b/tests/ui/unsupported-modifier.stderr
@@ -1,5 +1,5 @@
error: unsupported modifier
- --> $DIR/unsupported-modifier.rs:2:11
+ --> $DIR/unsupported-modifier.rs:4:11
|
-2 | fn [<a:pillow>]() {}
+4 | fn [<a:pillow>]() {}
| ^^^^^^^