From 41177158944274fd37d9160370611a61d68f24e7 Mon Sep 17 00:00:00 2001 From: Haibo Huang Date: Sun, 26 Jul 2020 23:48:05 -0700 Subject: Upgrade rust/crates/paste to 1.0.0 * Regenerate .bp file, now libpaste is a proc_macro, not an rlib. Test: make; atest -c --include-subdirs external/rust/crates Change-Id: I257008a9d94c636a6b0b6ebb36feb839f7a895cf --- .cargo_vcs_info.json | 2 +- .github/workflows/ci.yml | 23 +- Android.bp | 13 +- Cargo.toml | 8 +- Cargo.toml.orig | 10 +- METADATA | 4 +- README.md | 72 ++--- src/doc.rs | 92 ++++++ src/error.rs | 47 ++++ src/lib.rs | 529 +++++++++++++++++++++++++++++++---- tests/test.rs | 442 ----------------------------- tests/test_doc.rs | 44 +++ tests/test_expr.rs | 201 +++++++++++++ tests/test_item.rs | 269 ++++++++++++++++++ tests/ui/case-warning.rs | 4 +- tests/ui/case-warning.stderr | 6 +- tests/ui/env-empty.rs | 4 +- tests/ui/env-empty.stderr | 4 +- tests/ui/env-non-string.rs | 4 +- tests/ui/env-non-string.stderr | 4 +- tests/ui/env-suffix.rs | 4 +- tests/ui/env-suffix.stderr | 4 +- tests/ui/env-unexpected.rs | 4 +- tests/ui/env-unexpected.stderr | 4 +- tests/ui/invalid-ident.rs | 4 +- tests/ui/invalid-ident.stderr | 4 +- tests/ui/missing-paren-on-env.rs | 4 +- tests/ui/missing-paren-on-env.stderr | 4 +- tests/ui/no-env-var.rs | 4 +- tests/ui/no-env-var.stderr | 4 +- tests/ui/no-ident-after-colon.rs | 4 +- tests/ui/no-ident-after-colon.stderr | 4 +- tests/ui/unexpected-group.rs | 4 +- tests/ui/unexpected-group.stderr | 4 +- tests/ui/unexpected-modifier.rs | 4 +- tests/ui/unexpected-modifier.stderr | 4 +- tests/ui/unexpected-punct.rs | 4 +- tests/ui/unexpected-punct.stderr | 4 +- tests/ui/unsupported-literal.rs | 4 +- tests/ui/unsupported-literal.stderr | 4 +- tests/ui/unsupported-modifier.rs | 4 +- tests/ui/unsupported-modifier.stderr | 4 +- 42 files changed, 1265 insertions(+), 605 deletions(-) create mode 100644 src/doc.rs create mode 100644 src/error.rs delete mode 100644 tests/test.rs create mode 100644 tests/test_doc.rs create mode 100644 tests/test_expr.rs create mode 100644 tests/test_item.rs 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 "] 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 "] 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+.
## 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 []: &str = "success!"; } fn main() { - // Macro in expression position: inside a function body. assert_eq!( - paste::expr! { [].len() }, + paste! { [].len() }, 8, ); } @@ -50,34 +47,15 @@ fn main() {
-## 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![$( [] ),*] - } - }} -} - -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 [](&self) -> &str { @@ -139,6 +117,28 @@ The precise Unicode conversions are as defined by [`str::to_lowercase`] and
+## 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"] +``` + +
+ #### License 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 { + 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 = std::result::Result; + +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+. //! //!
//! //! # 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 []: &str = "success!"; //! } //! //! fn main() { -//! // Macro in expression position: inside a function body. //! assert_eq!( -//! paste::expr! { [].len() }, +//! paste! { [].len() }, //! 8, //! ); //! } @@ -45,34 +42,15 @@ //! //!

//! -//! # 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![$( [] ),*] -//! } -//! }} -//! } +//! 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 [](&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 +//! +//!
+//! +//! # 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 { + let mut expanded = TokenStream::new(); + let mut lookbehind = Lookbehind::Other; + let mut prev_none_group = None::; + 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> { + 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, + scope: Span, +) -> Result> { + 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 { + 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::(); + 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.rs b/tests/test.rs deleted file mode 100644 index e6dcdfa..0000000 --- a/tests/test.rs +++ /dev/null @@ -1,442 +0,0 @@ -mod test_basic { - struct Struct; - - paste::item! { - impl Struct { - fn []() {} - } - } - - #[test] - fn test() { - Struct::abc(); - } -} - -mod test_in_impl { - struct Struct; - - impl Struct { - paste::item! { - fn []() {} - } - } - - #[test] - fn test() { - Struct::abc(); - } -} - -#[test] -fn test_shared_hygiene() { - paste::expr! { - let [] = 1; - assert_eq!([], 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![$( [] ),*] - } - }} - } - - let routes = routes!(A, B); - assert_eq!(routes, vec!["/a", "/b"]); -} - -#[test] -fn test_integer() { - const CONST0: &'static str = "const0"; - - let pasted = paste::expr!([]); - assert_eq!(pasted, CONST0); -} - -#[test] -fn test_underscore() { - paste::expr! { - const A_B: usize = 0; - assert_eq!([], 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 []; - - let _ = Fmove; - } -} - -#[test] -fn test_literal_str() { - paste::expr! { - struct []; - - let _ = FooBar_Baz; - } -} - -#[test] -fn test_env_literal() { - paste::expr! { - struct []; - - let _ = Libenvbar; - } -} - -#[test] -fn test_env_present() { - paste::expr! { - struct []; - - let _ = Libpaste; - } -} - -#[test] -fn test_raw_identifier() { - paste::expr! { - struct []; - - 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 = [::f()]; - assert_eq!(x[0], 0); - } -} - -#[test] -fn test_local_variable() { - let yy = 0; - - paste::expr! { - assert_eq!([], 0); - } -} - -#[test] -fn test_empty() { - paste::expr! { - assert_eq!(stringify!([]), "yy"); - assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]"); - } -} - -mod test_none_delimited_single_ident { - macro_rules! m { - ($id:ident) => { - paste::item! { - fn f() -> &'static str { - stringify!($id) - } - } - }; - } - - m!(i32x4); - - #[test] - fn test() { - assert_eq!(f(), "i32x4"); - } -} - -mod test_none_delimited_single_lifetime { - macro_rules! m { - ($life:lifetime) => { - paste::item! { - pub struct S; - impl<$life> S { - fn f() {} - } - } - }; - } - - m!('a); - - #[test] - fn test() { - S::f(); - } -} - -mod test_to_lower { - macro_rules! m { - ($id:ident) => { - paste::item! { - fn [](_arg: u8) -> &'static str { - stringify!([<$id:lower>]) - } - } - }; - } - - m!(Test); - - #[test] - fn test_to_lower() { - assert_eq!(my_test_here(0), "test"); - } -} - -#[test] -fn test_env_to_lower() { - paste::expr! { - struct []; - - let _ = Libpaste; - } -} - -mod test_to_upper { - macro_rules! m { - ($id:ident) => { - paste::item! { - const []: &str = stringify!([<$id:upper>]); - } - }; - } - - m!(Test); - - #[test] - fn test_to_upper() { - assert_eq!(MY_TEST_HERE, "TEST"); - } -} - -#[test] -fn test_env_to_upper() { - paste::expr! { - const []: &str = "libpaste"; - - let _ = LIBPASTE; - } -} - -mod test_to_snake { - macro_rules! m { - ($id:ident) => { - paste::item! { - const DEFAULT_SNAKE: &str = stringify!([<$id:snake>]); - const LOWER_SNAKE: &str = stringify!([<$id:snake:lower>]); - const UPPER_SNAKE: &str = stringify!([<$id:snake:upper>]); - } - }; - } - - m!(ThisIsButATest); - - #[test] - fn test_to_snake() { - assert_eq!(DEFAULT_SNAKE, "this_is_but_a_test"); - assert_eq!(LOWER_SNAKE, "this_is_but_a_test"); - assert_eq!(UPPER_SNAKE, "THIS_IS_BUT_A_TEST"); - } -} - -#[test] -fn test_env_to_snake() { - paste::expr! { - const []: &str = "libpaste"; - - let _ = LIBPASTE; - } -} - -mod test_to_camel { - macro_rules! m { - ($id:ident) => { - paste::item! { - const DEFAULT_CAMEL: &str = stringify!([<$id:camel>]); - const LOWER_CAMEL: &str = stringify!([<$id:camel:lower>]); - const UPPER_CAMEL: &str = stringify!([<$id:camel:upper>]); - } - }; - } - - m!(this_is_but_a_test); - - #[test] - fn test_to_camel() { - assert_eq!(DEFAULT_CAMEL, "ThisIsButATest"); - assert_eq!(LOWER_CAMEL, "thisisbutatest"); - assert_eq!(UPPER_CAMEL, "THISISBUTATEST"); - } -} - -#[test] -fn test_env_to_camel() { - paste::expr! { - const []: &str = "libpaste"; - - let _ = LIBPaste; - } -} - -mod test_doc_expr { - // https://github.com/dtolnay/paste/issues/29 - - macro_rules! doc_expr { - ($doc:expr) => { - paste::item! { - #[doc = $doc] - pub struct S; - } - }; - } - - doc_expr!(stringify!()); - - #[test] - fn test_doc_expr() { - let _: S; - } -} - -mod test_type_in_path { - // https://github.com/dtolnay/paste/issues/31 - - mod keys { - #[derive(Default)] - pub struct Mib(std::marker::PhantomData); - } - - macro_rules! types { - ($mib:ty) => { - paste::item! { - #[derive(Default)] - pub struct S(pub keys::$mib); - } - }; - } - - macro_rules! write { - ($fn:ident, $field:ty) => { - paste::item! { - pub fn $fn() -> $field { - $field::default() - } - } - }; - } - - types! {Mib<[usize; 2]>} - write! {get_a, keys::Mib} - write! {get_b, usize} - - #[test] - fn test_type_in_path() { - let _: S; - let _ = get_a; - let _ = get_b; - } -} - -mod test_type_in_fn_arg { - // https://github.com/dtolnay/paste/issues/38 - - fn _jit_address(_node: ()) {} - - macro_rules! jit_reexport { - ($fn:ident, $arg:ident : $typ:ty) => { - paste::item! { - pub fn $fn($arg: $typ) { - [<_jit_ $fn>]($arg); - } - } - }; - } - - jit_reexport!(address, node: ()); - - #[test] - fn test_type_in_fn_arg() { - let _ = address; - } -} - -mod test_pat_in_expr_position { - // https://github.com/xiph/rav1e/pull/2324/files - - macro_rules! rav1e_bad { - ($e:pat) => { - paste::item! { - #[test] - fn test() { - let _ = $e; - } - } - }; - } - - 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/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 [] = 1; + assert_eq!([], 1); + } +} + +#[test] +fn test_repeat() { + const ROCKET_A: &'static str = "/a"; + const ROCKET_B: &'static str = "/b"; + + macro_rules! routes { + ($($route:ident),*) => {{ + paste! { + vec![$( [] ),*] + } + }} + } + + let routes = routes!(A, B); + assert_eq!(routes, vec!["/a", "/b"]); +} + +#[test] +fn test_integer() { + const CONST0: &'static str = "const0"; + + let pasted = paste!([]); + assert_eq!(pasted, CONST0); +} + +#[test] +fn test_underscore() { + paste! { + const A_B: usize = 0; + assert_eq!([], 0); + } +} + +#[test] +fn test_lifetime() { + paste! { + #[allow(dead_code)] + struct S<[<'d e>]> { + q: &[<'d e>] str, + } + } +} + +#[test] +fn test_keyword() { + paste! { + struct []; + + let _ = Fmove; + } +} + +#[test] +fn test_literal_str() { + paste! { + #[allow(non_camel_case_types)] + struct []; + + let _ = FooBar_Baz; + } +} + +#[test] +fn test_env_literal() { + paste! { + struct []; + + let _ = Libenvbar; + } +} + +#[test] +fn test_env_present() { + paste! { + struct []; + + let _ = Libpaste; + } +} + +#[test] +fn test_raw_identifier() { + paste! { + struct []; + + 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 = [::f()]; + assert_eq!(x[0], 0); + } +} + +#[test] +fn test_local_variable() { + let yy = 0; + + paste! { + assert_eq!([], 0); + } +} + +#[test] +fn test_empty() { + paste! { + assert_eq!(stringify!([]), "yy"); + assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]"); + } +} + +#[test] +fn test_env_to_lower() { + paste! { + struct []; + + let _ = Libpaste; + } +} + +#[test] +fn test_env_to_upper() { + paste! { + const []: &str = "libpaste"; + + let _ = LIBPASTE; + } +} + +#[test] +fn test_env_to_snake() { + paste! { + const []: &str = "libpaste"; + + let _ = LIBPASTE; + } +} + +#[test] +fn test_env_to_camel() { + paste! { + #[allow(non_upper_case_globals)] + const []: &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_item.rs b/tests/test_item.rs new file mode 100644 index 0000000..86c98a9 --- /dev/null +++ b/tests/test_item.rs @@ -0,0 +1,269 @@ +mod test_basic { + use paste::paste; + + struct Struct; + + paste! { + impl Struct { + fn []() {} + } + } + + #[test] + fn test() { + Struct::abc(); + } +} + +mod test_in_impl { + use paste::paste; + + struct Struct; + + impl Struct { + paste! { + fn []() {} + } + } + + #[test] + fn test() { + Struct::abc(); + } +} + +mod test_none_delimited_single_ident { + use paste::paste; + + macro_rules! m { + ($id:ident) => { + paste! { + fn f() -> &'static str { + stringify!($id) + } + } + }; + } + + m!(i32x4); + + #[test] + fn test() { + assert_eq!(f(), "i32x4"); + } +} + +mod test_none_delimited_single_lifetime { + use paste::paste; + + macro_rules! m { + ($life:lifetime) => { + paste! { + pub struct S; + impl<$life> S { + fn f() {} + } + } + }; + } + + m!('a); + + #[test] + fn test() { + S::f(); + } +} + +mod test_to_lower { + use paste::paste; + + macro_rules! m { + ($id:ident) => { + paste! { + fn [](_arg: u8) -> &'static str { + stringify!([<$id:lower>]) + } + } + }; + } + + m!(Test); + + #[test] + fn test_to_lower() { + assert_eq!(my_test_here(0), "test"); + } +} + +mod test_to_upper { + use paste::paste; + + macro_rules! m { + ($id:ident) => { + paste! { + const []: &str = stringify!([<$id:upper>]); + } + }; + } + + m!(Test); + + #[test] + fn test_to_upper() { + assert_eq!(MY_TEST_HERE, "TEST"); + } +} + +mod test_to_snake { + use paste::paste; + + macro_rules! m { + ($id:ident) => { + paste! { + const DEFAULT_SNAKE: &str = stringify!([<$id:snake>]); + const LOWER_SNAKE: &str = stringify!([<$id:snake:lower>]); + const UPPER_SNAKE: &str = stringify!([<$id:snake:upper>]); + } + }; + } + + m!(ThisIsButATest); + + #[test] + fn test_to_snake() { + assert_eq!(DEFAULT_SNAKE, "this_is_but_a_test"); + assert_eq!(LOWER_SNAKE, "this_is_but_a_test"); + assert_eq!(UPPER_SNAKE, "THIS_IS_BUT_A_TEST"); + } +} + +mod test_to_camel { + use paste::paste; + + macro_rules! m { + ($id:ident) => { + paste! { + const DEFAULT_CAMEL: &str = stringify!([<$id:camel>]); + const LOWER_CAMEL: &str = stringify!([<$id:camel:lower>]); + const UPPER_CAMEL: &str = stringify!([<$id:camel:upper>]); + } + }; + } + + m!(this_is_but_a_test); + + #[test] + fn test_to_camel() { + assert_eq!(DEFAULT_CAMEL, "ThisIsButATest"); + assert_eq!(LOWER_CAMEL, "thisisbutatest"); + assert_eq!(UPPER_CAMEL, "THISISBUTATEST"); + } +} + +mod test_doc_expr { + // https://github.com/dtolnay/paste/issues/29 + + use paste::paste; + + macro_rules! doc_expr { + ($doc:expr) => { + paste! { + #[doc = $doc] + pub struct S; + } + }; + } + + doc_expr!(stringify!()); + + #[test] + fn test_doc_expr() { + let _: S; + } +} + +mod test_type_in_path { + // https://github.com/dtolnay/paste/issues/31 + + use paste::paste; + + mod keys { + #[derive(Default)] + pub struct Mib(std::marker::PhantomData); + } + + macro_rules! types { + ($mib:ty) => { + paste! { + #[derive(Default)] + pub struct S(pub keys::$mib); + } + }; + } + + macro_rules! write { + ($fn:ident, $field:ty) => { + paste! { + pub fn $fn() -> $field { + $field::default() + } + } + }; + } + + types! {Mib<[usize; 2]>} + write! {get_a, keys::Mib} + write! {get_b, usize} + + #[test] + fn test_type_in_path() { + let _: S; + let _ = get_a; + let _ = get_b; + } +} + +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! { + pub fn $fn($arg: $typ) { + [<_jit_ $fn>]($arg); + } + } + }; + } + + jit_reexport!(address, node: ()); + + #[test] + fn test_type_in_fn_arg() { + let _ = address; + } +} + +mod test_pat_in_expr_position { + // https://github.com/xiph/rav1e/pull/2324/files + + use paste::paste; + + macro_rules! rav1e_bad { + ($e:pat) => { + paste! { + #[test] + fn test() { + let _ = $e; + } + } + }; + } + + rav1e_bad!(std::fmt::Error); +} 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 []() {} } }; 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 []() {} +8 | pub fn []() {} | ^^^^^^^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^^^^^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^^^^^^^^^^^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^ 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 []() {} } 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 []() {} +4 | fn []() {} | ^^^^^^^ -- cgit v1.2.3