From 754d1539aa9f4a4f42ca699d51722c66c9feea04 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 6 May 2023 09:39:26 +0200 Subject: [PATCH 1/9] Add UI tests Signed-off-by: Oliver Tale-Yazdi --- .gitignore | 2 ++ README.md | 8 +++++--- src/lib.rs | 2 +- tests/Cargo.toml | 5 +++++ tests/derive/Cargo.toml | 13 +++++++++++++ tests/derive/src/lib.rs | 26 ++++++++++++++++++++++++++ tests/ui/Cargo.toml | 9 +++++++++ tests/ui/src/lib.rs | 17 +++++++++++++++++ tests/ui/src/no-warn/derive_no_span.rs | 10 ++++++++++ tests/ui/src/warn/derive_basic.rs | 8 ++++++++ tests/ui/src/warn/derive_basic.stderr | 11 +++++++++++ 11 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 tests/Cargo.toml create mode 100644 tests/derive/Cargo.toml create mode 100644 tests/derive/src/lib.rs create mode 100644 tests/ui/Cargo.toml create mode 100644 tests/ui/src/lib.rs create mode 100644 tests/ui/src/no-warn/derive_no_span.rs create mode 100644 tests/ui/src/warn/derive_basic.rs create mode 100644 tests/ui/src/warn/derive_basic.stderr diff --git a/.gitignore b/.gitignore index cd23ebe..455fef8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target/ +**/target + Cargo.lock **/*.rs.bk diff --git a/README.md b/README.md index 3b56824..690f334 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Building a warning is easy with the builder pattern. ```rust use proc_macro_warning::Warning; + let warning = Warning::new_deprecated("my_macro") .old("my_macro()") .new("my_macro::new()") @@ -35,6 +36,8 @@ let warning = Warning::new_deprecated("my_macro") let tokens = quote::quote!(#warning); ``` +This works in derive-macros, but you **must** put in a span, otherwise it will not show up in the compile output. + ## Used In Substrate (since [#13798](https://github.com/paritytech/substrate/pull/13798)) uses this to emit warnings for its FRAME eDSL on deprecated behaviour. @@ -69,13 +72,12 @@ warning: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: | ``` - ## License Licensed under either of at your own choice: -* GNU GENERAL PUBLIC LICENSE, Version 3 ([LICENSE-GPL3](./LICENSE-GPL3) or https://www.gnu.org/licenses/gpl-3.0.txt) -* Apache License, Version 2.0 ([LICENSE-APACHE2](/LICENSE-APACHE2) or https://www.apache.org/licenses/LICENSE-2.0.txt). +* GNU GENERAL PUBLIC LICENSE, Version 3 ([LICENSE-GPL3](./LICENSE-GPL3) or [gnu.org](https://www.gnu.org/licenses/gpl-3.0.txt>)) +* Apache License, Version 2.0 ([LICENSE-APACHE2](/LICENSE-APACHE2) or [apache.org](https://www.apache.org/licenses/LICENSE-2.0.txt>)). ### Contribution diff --git a/src/lib.rs b/src/lib.rs index 92c7750..ec2631e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: (GPL-3.0 or Apache-2.0) */ -//! Emit warnings from inside proc macros. +#![doc = include_str!("../README.md")] use core::ops::Deref; use proc_macro2::Span; diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000..061fc0b --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "derive", + "ui" +] diff --git a/tests/derive/Cargo.toml b/tests/derive/Cargo.toml new file mode 100644 index 0000000..1e3a3ce --- /dev/null +++ b/tests/derive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "derive" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +proc-macro = true + +[dependencies] +proc-macro-warning = { path = "../../" } +quote = "1.0.26" +syn = "2.0.15" diff --git a/tests/derive/src/lib.rs b/tests/derive/src/lib.rs new file mode 100644 index 0000000..d980488 --- /dev/null +++ b/tests/derive/src/lib.rs @@ -0,0 +1,26 @@ +use proc_macro::TokenStream; +use syn::spanned::Spanned; + +#[proc_macro_derive(Deprecated)] +pub fn deprecated(input: TokenStream) -> TokenStream { + impl_dep(input, true) +} + +#[proc_macro_derive(DeprecatedNoSpan)] +pub fn deprecated2(input: TokenStream) -> TokenStream { + impl_dep(input, false) +} + +fn impl_dep(input: TokenStream, span: bool) -> TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + let warning = proc_macro_warning::Warning::new_deprecated("test") + .old("foo").new("bar"); + let warning = if span { + warning.span(input.span()) + } else { + warning + }.build(); + + quote::quote!{ #warning }.into() +} diff --git a/tests/ui/Cargo.toml b/tests/ui/Cargo.toml new file mode 100644 index 0000000..7c1c9b6 --- /dev/null +++ b/tests/ui/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ui" +version = "0.1.0" +edition = "2021" +publish = false + +[dev-dependencies] +trybuild = "1.0.80" +derive = { path = "../derive" } diff --git a/tests/ui/src/lib.rs b/tests/ui/src/lib.rs new file mode 100644 index 0000000..dc8b8e9 --- /dev/null +++ b/tests/ui/src/lib.rs @@ -0,0 +1,17 @@ +#[test] +#[cfg(test)] +fn ui_warm() { + std::env::set_var("RUSTFLAGS", "--deny warnings"); + let t = trybuild::TestCases::new(); + + t.compile_fail("src/warn/*.rs"); +} + +#[test] +#[cfg(test)] +fn ui_no_warn() { + std::env::set_var("RUSTFLAGS", "--deny warnings"); + let t = trybuild::TestCases::new(); + + t.pass("src/no-warn/*.rs"); +} diff --git a/tests/ui/src/no-warn/derive_no_span.rs b/tests/ui/src/no-warn/derive_no_span.rs new file mode 100644 index 0000000..05a0bb7 --- /dev/null +++ b/tests/ui/src/no-warn/derive_no_span.rs @@ -0,0 +1,10 @@ +//! Without a span no warning will be printed. + +#[derive(derive::DeprecatedNoSpan)] +struct Test { + +} + +fn main() { + let _ = Test { }; +} diff --git a/tests/ui/src/warn/derive_basic.rs b/tests/ui/src/warn/derive_basic.rs new file mode 100644 index 0000000..31f5b3f --- /dev/null +++ b/tests/ui/src/warn/derive_basic.rs @@ -0,0 +1,8 @@ +#[derive(derive::Deprecated)] +struct Test { + +} + +fn main() { + let _ = Test { }; +} diff --git a/tests/ui/src/warn/derive_basic.stderr b/tests/ui/src/warn/derive_basic.stderr new file mode 100644 index 0000000..eaff881 --- /dev/null +++ b/tests/ui/src/warn/derive_basic.stderr @@ -0,0 +1,11 @@ +error: use of deprecated constant `test::_w`: + It is deprecated to foo. + Please instead bar. + --> src/warn/derive_basic.rs:2:1 + | +2 | / struct Test { +3 | | +4 | | } + | |_^ + | + = note: `-D deprecated` implied by `-D warnings` From 349eb67b5b4696d72bde066884b7c3afd2f36e43 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 6 May 2023 10:23:25 +0200 Subject: [PATCH 2/9] Add docs and UI tests Signed-off-by: Oliver Tale-Yazdi --- Cargo.toml | 23 ++++----------- README.md | 20 ++++++++++--- proc-macro-warning/Cargo.toml | 21 ++++++++++++++ {src => proc-macro-warning/src}/lib.rs | 24 ++++++++++------ {src => proc-macro-warning/src}/test.rs | 2 +- {tests => ui-tests}/Cargo.toml | 0 {tests => ui-tests}/derive/Cargo.toml | 2 +- {tests => ui-tests}/derive/src/lib.rs | 4 ++- {tests => ui-tests}/ui/Cargo.toml | 0 {tests => ui-tests}/ui/src/lib.rs | 2 ++ .../ui/src/no-warn/derive_no_span.rs | 0 .../ui/src/warn/derive_basic.rs | 5 +--- .../ui/src/warn/derive_basic.stderr | 6 ++-- ui-tests/ui/src/warn/derive_twice_errors.rs | 8 ++++++ .../ui/src/warn/derive_twice_errors.stderr | 28 +++++++++++++++++++ 15 files changed, 105 insertions(+), 40 deletions(-) create mode 100644 proc-macro-warning/Cargo.toml rename {src => proc-macro-warning/src}/lib.rs (89%) rename {src => proc-macro-warning/src}/test.rs (95%) rename {tests => ui-tests}/Cargo.toml (100%) rename {tests => ui-tests}/derive/Cargo.toml (71%) rename {tests => ui-tests}/derive/src/lib.rs (90%) rename {tests => ui-tests}/ui/Cargo.toml (100%) rename {tests => ui-tests}/ui/src/lib.rs (90%) rename {tests => ui-tests}/ui/src/no-warn/derive_no_span.rs (100%) rename {tests => ui-tests}/ui/src/warn/derive_basic.rs (53%) rename {tests => ui-tests}/ui/src/warn/derive_basic.stderr (83%) create mode 100644 ui-tests/ui/src/warn/derive_twice_errors.rs create mode 100644 ui-tests/ui/src/warn/derive_twice_errors.stderr diff --git a/Cargo.toml b/Cargo.toml index a3abc5d..0f69d47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,7 @@ -[package] -name = "proc-macro-warning" -version = "0.3.1" -edition = "2021" -license = "GPL-3.0 OR Apache-2.0" -authors = ["Oliver Tale-Yazdi "] -description = "Emit warnings from inside proc macros." -repository = "https://github.com/ggwpez/proc-macro-warning" +[workspace] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -proc-macro2 = { version = "1.0.56", default-features = false } -quote = { version = "1.0.26", default-features = false } -syn = { version = "2.0.15", default-features = false } - -[features] -default = [] +members = [ + "proc-macro-warning", + "ui-tests/derive", + "ui-tests/ui", +] diff --git a/README.md b/README.md index 690f334..68e3fe1 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,25 @@ let warning = Warning::new_deprecated("my_macro") let tokens = quote::quote!(#warning); ``` -This works in derive-macros, but you **must** put in a span, otherwise it will not show up in the compile output. +This works in derive-macros, but you **must** set a span; otherwise it will not show up in the compile output. + +The difference to a `#[deprecated]` attribute is that it emits the warning either way. For example when creating a custom `Deprecated` derive macro, it will warn without the struct being constructed. + +```rust +#[derive(derive::Deprecated)] +struct Test {} + +fn main() { + // Warning triggers although we never used `Test`. + // Otherwise use a normal `#[deprecated]`. +} +``` ## Used In -Substrate (since [#13798](https://github.com/paritytech/substrate/pull/13798)) uses this to emit warnings for its FRAME eDSL on deprecated behaviour. +Substrate uses it to emit warnings for its eDSL (FRAME) on deprecated behaviour. The integration was done in [#13798](https://github.com/paritytech/substrate/pull/13798) and shows how to use these warnings in macro expansion. -For example not putting a `call_index` on your functions produces: +The warnings are uniformly formatted and have consistent grammar: ```pre warning: use of deprecated constant `pallet::warnings::ImplicitCallIndex_0::_w`: It is deprecated to use implicit call indices. @@ -58,7 +70,7 @@ warning: use of deprecated constant `pallet::warnings::ImplicitCallIndex_0::_w`: | ``` -Or using a hard-coded weight: +A different one: ```pre warning: use of deprecated constant `pallet::warnings::ConstantWeight_0::_w`: It is deprecated to use hard-coded constant as call weight. diff --git a/proc-macro-warning/Cargo.toml b/proc-macro-warning/Cargo.toml new file mode 100644 index 0000000..961821d --- /dev/null +++ b/proc-macro-warning/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "proc-macro-warning" +version = "0.3.1" +edition = "2021" +license = "GPL-3.0 OR Apache-2.0" +authors = ["Oliver Tale-Yazdi "] +description = "Emit warnings from inside proc macros." +repository = "https://github.com/ggwpez/proc-macro-warning" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = { version = "1.0.56", default-features = false } +quote = { version = "1.0.26", default-features = false } +syn = { version = "2.0.15", default-features = false } + +[dev-dependencies] +derive = { path = "../ui-tests/derive" } + +[features] +default = [] diff --git a/src/lib.rs b/proc-macro-warning/src/lib.rs similarity index 89% rename from src/lib.rs rename to proc-macro-warning/src/lib.rs index ec2631e..0eed591 100644 --- a/src/lib.rs +++ b/proc-macro-warning/src/lib.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: (GPL-3.0 or Apache-2.0) */ -#![doc = include_str!("../README.md")] +#![doc = include_str!("../../README.md")] use core::ops::Deref; use proc_macro2::Span; @@ -20,24 +20,25 @@ pub struct Warning { pub span: Span, } -/// Gradually build a "deprecated" `Warning`. +/// Gradually build a *deprecation* `Warning`. /// /// # Example -/// ``` +/// +/// ```rust /// use proc_macro_warning::Warning; /// /// let warning = Warning::new_deprecated("my_macro") /// .old("my_macro()") /// .new("my_macro::new()") /// .help_link("https:://example.com") +/// // Normally you use the input span, but this is an example: /// .span(proc_macro2::Span::call_site()) /// .build(); /// -/// // Use the warning in a proc macro -/// let tokens = quote::quote!(#warning); +/// let mut warnings = vec![warning]; +/// // When adding more, you will need to build each with `.index`. /// -/// let warnings = vec![warning]; -/// // In a proc macro you would expand them inside a module: +/// // In a proc macro you can expand them in a private module: /// quote::quote! { /// mod warnings { /// #( @@ -180,7 +181,7 @@ impl ToTokens for Warning { let q = quote_spanned!(self.span => /// This function should not be called and only exists to emit a compiler warning. /// - /// It is a No-OP if you want try it anyway ;) + /// It is a No-OP in any case. #[allow(dead_code)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] @@ -194,3 +195,10 @@ impl ToTokens for Warning { q.to_tokens(stream); } } + +impl Warning { + /// Consume self and quote it, according to its span, into a TokenStream. + pub fn into_token_stream(self) -> proc_macro2::TokenStream { + quote::quote! { #self }.into() + } +} diff --git a/src/test.rs b/proc-macro-warning/src/test.rs similarity index 95% rename from src/test.rs rename to proc-macro-warning/src/test.rs index dc7f04d..e7624a7 100644 --- a/src/test.rs +++ b/proc-macro-warning/src/test.rs @@ -22,7 +22,7 @@ fn example_works() { let want_tokens = quote!( /// This function should not be called and only exists to emit a compiler warning. /// - /// It is a No-OP if you want try it anyway ;) + /// It is a No-OP in any case. #[allow(dead_code)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] diff --git a/tests/Cargo.toml b/ui-tests/Cargo.toml similarity index 100% rename from tests/Cargo.toml rename to ui-tests/Cargo.toml diff --git a/tests/derive/Cargo.toml b/ui-tests/derive/Cargo.toml similarity index 71% rename from tests/derive/Cargo.toml rename to ui-tests/derive/Cargo.toml index 1e3a3ce..005f686 100644 --- a/tests/derive/Cargo.toml +++ b/ui-tests/derive/Cargo.toml @@ -8,6 +8,6 @@ publish = false proc-macro = true [dependencies] -proc-macro-warning = { path = "../../" } +proc-macro-warning = { path = "../../proc-macro-warning" } quote = "1.0.26" syn = "2.0.15" diff --git a/tests/derive/src/lib.rs b/ui-tests/derive/src/lib.rs similarity index 90% rename from tests/derive/src/lib.rs rename to ui-tests/derive/src/lib.rs index d980488..2b4b294 100644 --- a/tests/derive/src/lib.rs +++ b/ui-tests/derive/src/lib.rs @@ -1,3 +1,5 @@ +//! TESTING ONLY - DO NOT USE. + use proc_macro::TokenStream; use syn::spanned::Spanned; @@ -22,5 +24,5 @@ fn impl_dep(input: TokenStream, span: bool) -> TokenStream { warning }.build(); - quote::quote!{ #warning }.into() + warning.into_token_stream().into() } diff --git a/tests/ui/Cargo.toml b/ui-tests/ui/Cargo.toml similarity index 100% rename from tests/ui/Cargo.toml rename to ui-tests/ui/Cargo.toml diff --git a/tests/ui/src/lib.rs b/ui-tests/ui/src/lib.rs similarity index 90% rename from tests/ui/src/lib.rs rename to ui-tests/ui/src/lib.rs index dc8b8e9..649a475 100644 --- a/tests/ui/src/lib.rs +++ b/ui-tests/ui/src/lib.rs @@ -1,3 +1,5 @@ +//! TESTING ONLY - DO NOT USE. + #[test] #[cfg(test)] fn ui_warm() { diff --git a/tests/ui/src/no-warn/derive_no_span.rs b/ui-tests/ui/src/no-warn/derive_no_span.rs similarity index 100% rename from tests/ui/src/no-warn/derive_no_span.rs rename to ui-tests/ui/src/no-warn/derive_no_span.rs diff --git a/tests/ui/src/warn/derive_basic.rs b/ui-tests/ui/src/warn/derive_basic.rs similarity index 53% rename from tests/ui/src/warn/derive_basic.rs rename to ui-tests/ui/src/warn/derive_basic.rs index 31f5b3f..b14fcd1 100644 --- a/tests/ui/src/warn/derive_basic.rs +++ b/ui-tests/ui/src/warn/derive_basic.rs @@ -1,8 +1,5 @@ #[derive(derive::Deprecated)] -struct Test { - -} +struct Test; fn main() { - let _ = Test { }; } diff --git a/tests/ui/src/warn/derive_basic.stderr b/ui-tests/ui/src/warn/derive_basic.stderr similarity index 83% rename from tests/ui/src/warn/derive_basic.stderr rename to ui-tests/ui/src/warn/derive_basic.stderr index eaff881..abd9ebb 100644 --- a/tests/ui/src/warn/derive_basic.stderr +++ b/ui-tests/ui/src/warn/derive_basic.stderr @@ -3,9 +3,7 @@ error: use of deprecated constant `test::_w`: Please instead bar. --> src/warn/derive_basic.rs:2:1 | -2 | / struct Test { -3 | | -4 | | } - | |_^ +2 | struct Test; + | ^^^^^^^^^^^^ | = note: `-D deprecated` implied by `-D warnings` diff --git a/ui-tests/ui/src/warn/derive_twice_errors.rs b/ui-tests/ui/src/warn/derive_twice_errors.rs new file mode 100644 index 0000000..aa46688 --- /dev/null +++ b/ui-tests/ui/src/warn/derive_twice_errors.rs @@ -0,0 +1,8 @@ +#[derive(derive::Deprecated)] +struct Test; + +#[derive(derive::Deprecated)] +struct Test2; + +fn main() { +} diff --git a/ui-tests/ui/src/warn/derive_twice_errors.stderr b/ui-tests/ui/src/warn/derive_twice_errors.stderr new file mode 100644 index 0000000..190b49c --- /dev/null +++ b/ui-tests/ui/src/warn/derive_twice_errors.stderr @@ -0,0 +1,28 @@ +error[E0428]: the name `test` is defined multiple times + --> src/warn/derive_twice_errors.rs:5:1 + | +2 | struct Test; + | ------------ previous definition of the value `test` here +... +5 | struct Test2; + | ^^^^^^^^^^^^^ `test` redefined here + | + = note: `test` must be defined only once in the value namespace of this module + +error: use of deprecated constant `test::_w`: + It is deprecated to foo. + Please instead bar. + --> src/warn/derive_twice_errors.rs:2:1 + | +2 | struct Test; + | ^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` + +error: use of deprecated constant `test::_w`: + It is deprecated to foo. + Please instead bar. + --> src/warn/derive_twice_errors.rs:5:1 + | +5 | struct Test2; + | ^^^^^^^^^^^^^ From 430e8a7074d8e586cd3ce23658b9d22758a057e9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 13:31:09 +0200 Subject: [PATCH 3/9] Add FormattedWarning Signed-off-by: Oliver Tale-Yazdi --- proc-macro-warning/src/lib.rs | 73 ++++++++++++++++--- ui-tests/derive/src/lib.rs | 20 +++++ ui-tests/ui/src/warn/derive_raw.rs | 5 ++ ui-tests/ui/src/warn/derive_raw.stderr | 10 +++ .../ui/src/warn/derive_twice_errors.stderr | 29 ++------ 5 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 ui-tests/ui/src/warn/derive_raw.rs create mode 100644 ui-tests/ui/src/warn/derive_raw.stderr diff --git a/proc-macro-warning/src/lib.rs b/proc-macro-warning/src/lib.rs index 0eed591..d33c36c 100644 --- a/proc-macro-warning/src/lib.rs +++ b/proc-macro-warning/src/lib.rs @@ -12,6 +12,7 @@ use quote::{quote_spanned, ToTokens}; mod test; /// Creates a compile-time warning for proc macro use. See [DeprecatedWarningBuilder] for usage. +#[derive(Clone)] pub struct Warning { pub name: String, pub index: Option, @@ -20,6 +21,43 @@ pub struct Warning { pub span: Span, } +/// A compile-time warning that was already subject to formatting. +/// +/// Any content will be pasted as-is. +#[derive(Clone)] +pub enum FormattedWarning { + /// A *deprecation* warning. + Deprecated { + /// Unique name of this warning. + /// + /// Must be unique in the case that multiple of these warnings are emitted, for example by + /// appending a counter. + name: syn::Ident, + /// The exact note to be used for `note = ""`. + note: String, + /// The span of the warning. + /// + /// Should be set to the original location of where the warning should be emitted. + span: Option, + }, +} + +impl FormattedWarning { + /// Create a new deprecated warning that already was formatted by the caller. + #[must_use] + pub fn new_deprecated<'a, S, T>(name: S, note: T, span: Span) -> Self + where + S: Into<&'a str>, + T: Into, + { + Self::Deprecated { + name: syn::Ident::new(name.into(), span), + note: note.into(), + span: Some(span), + } + } +} + /// Gradually build a *deprecation* `Warning`. /// /// # Example @@ -127,7 +165,7 @@ impl DeprecatedWarningBuilder { } impl Warning { - /// Create a new *raw* warnings. + /// Create a new *raw* warning. pub fn new_raw( name: String, index: Option, @@ -173,12 +211,30 @@ impl Warning { } } +impl Into for Warning { + fn into(self) -> FormattedWarning { + FormattedWarning::Deprecated { + name: self.final_name(), + note: self.final_message(), + span: Some(self.span), + } + } +} + impl ToTokens for Warning { fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) { - let name = self.final_name(); - let message = self.final_message(); + let formatted: FormattedWarning = self.clone().into(); + formatted.to_tokens(stream); + } +} + +impl ToTokens for FormattedWarning { + fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) { + let (name, note, span) = match self { + FormattedWarning::Deprecated { name, note, span } => (name, note, span), + }; - let q = quote_spanned!(self.span => + let q = quote_spanned!(*span => /// This function should not be called and only exists to emit a compiler warning. /// /// It is a No-OP in any case. @@ -186,7 +242,7 @@ impl ToTokens for Warning { #[allow(non_camel_case_types)] #[allow(non_snake_case)] fn #name() { - #[deprecated(note = #message)] + #[deprecated(note = #note)] #[allow(non_upper_case_globals)] const _w: () = (); let _ = _w; @@ -195,10 +251,3 @@ impl ToTokens for Warning { q.to_tokens(stream); } } - -impl Warning { - /// Consume self and quote it, according to its span, into a TokenStream. - pub fn into_token_stream(self) -> proc_macro2::TokenStream { - quote::quote! { #self }.into() - } -} diff --git a/ui-tests/derive/src/lib.rs b/ui-tests/derive/src/lib.rs index 2b4b294..0cf1d22 100644 --- a/ui-tests/derive/src/lib.rs +++ b/ui-tests/derive/src/lib.rs @@ -2,6 +2,7 @@ use proc_macro::TokenStream; use syn::spanned::Spanned; +use quote::ToTokens; #[proc_macro_derive(Deprecated)] pub fn deprecated(input: TokenStream) -> TokenStream { @@ -26,3 +27,22 @@ fn impl_dep(input: TokenStream, span: bool) -> TokenStream { warning.into_token_stream().into() } + +#[proc_macro_derive(DeprecatedRaw)] +pub fn deprecated_raw(input: TokenStream) -> TokenStream { + impl_dep_raw(input) +} + +fn impl_dep_raw(input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + let warning = proc_macro_warning::FormattedWarning::new_deprecated( + "VeryOldStuff", + "\nMy message do noooooooooooooooooooooooooooooot formaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat +or chaaaaaaaaaaaaange this, also no line breaks please ;) +other veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy looooooooooooooooooooooong lineeeeeeeeeeeeeeee", + input.span(), + ); + + warning.into_token_stream().into() +} diff --git a/ui-tests/ui/src/warn/derive_raw.rs b/ui-tests/ui/src/warn/derive_raw.rs new file mode 100644 index 0000000..11f90d0 --- /dev/null +++ b/ui-tests/ui/src/warn/derive_raw.rs @@ -0,0 +1,5 @@ +#[derive(derive::DeprecatedRaw)] +struct Test; + +fn main() { +} diff --git a/ui-tests/ui/src/warn/derive_raw.stderr b/ui-tests/ui/src/warn/derive_raw.stderr new file mode 100644 index 0000000..a8b6551 --- /dev/null +++ b/ui-tests/ui/src/warn/derive_raw.stderr @@ -0,0 +1,10 @@ +error: use of deprecated constant `VeryOldStuff::_w`: + My message do noooooooooooooooooooooooooooooot formaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat + or chaaaaaaaaaaaaange this, also no line breaks please ;) + other veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy looooooooooooooooooooooong lineeeeeeeeeeeeeeee + --> src/warn/derive_raw.rs:2:1 + | +2 | struct Test; + | ^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` diff --git a/ui-tests/ui/src/warn/derive_twice_errors.stderr b/ui-tests/ui/src/warn/derive_twice_errors.stderr index 190b49c..eaef3d4 100644 --- a/ui-tests/ui/src/warn/derive_twice_errors.stderr +++ b/ui-tests/ui/src/warn/derive_twice_errors.stderr @@ -1,28 +1,11 @@ error[E0428]: the name `test` is defined multiple times - --> src/warn/derive_twice_errors.rs:5:1 + --> src/warn/derive_twice_errors.rs:4:10 | -2 | struct Test; - | ------------ previous definition of the value `test` here +1 | #[derive(derive::Deprecated)] + | ------------------ previous definition of the value `test` here ... -5 | struct Test2; - | ^^^^^^^^^^^^^ `test` redefined here +4 | #[derive(derive::Deprecated)] + | ^^^^^^^^^^^^^^^^^^ `test` redefined here | = note: `test` must be defined only once in the value namespace of this module - -error: use of deprecated constant `test::_w`: - It is deprecated to foo. - Please instead bar. - --> src/warn/derive_twice_errors.rs:2:1 - | -2 | struct Test; - | ^^^^^^^^^^^^ - | - = note: `-D deprecated` implied by `-D warnings` - -error: use of deprecated constant `test::_w`: - It is deprecated to foo. - Please instead bar. - --> src/warn/derive_twice_errors.rs:5:1 - | -5 | struct Test2; - | ^^^^^^^^^^^^^ + = note: this error originates in the derive macro `derive::Deprecated` (in Nightly builds, run with -Z macro-backtrace for more info) From 6ebfc956a9623c379291542aca0586005903a999 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 13:39:49 +0200 Subject: [PATCH 4/9] Update README Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/rust.yml | 2 +- .gitignore | 2 ++ README.md | 20 +++++++++++++ proc-macro-warning/src/lib.rs | 3 +- .../ui/src/warn/derive_twice_errors.stderr | 29 +++++++++++++++---- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 312bf69..b574d7a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: run: cargo build --all-targets --all-features # --locked TODO - name: Test - run: cargo test --locked --all-targets --all-features + run: TRYBUILD=overwrite cargo test --locked --all-targets --all-features - name: Clippy run: cargo clippy --locked --all-targets --all-features -- -D warnings diff --git a/.gitignore b/.gitignore index 455fef8..48be6a6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ Cargo.lock **/*.rs.bk + +.vscode/ diff --git a/README.md b/README.md index 68e3fe1..c0161c3 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,26 @@ fn main() { } ``` +## Un-opinionated Formatting + +The normal aforementioned way of creating a warning will impose specific unified grammar and formatting rules. +You can opt out of this and use your own instead by using `FormattedWarning::new_deprecated`: + +```rust +use proc_macro_warning::FormattedWarning; + +let warning = FormattedWarning::new_deprecated( + "my_macro", + "looooooooooooooooooooooooooooooong line that will not be brokeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeen ;)", + proc_macro2::Span::call_site(), + ); + +// Use the warning in a proc macro +let tokens = quote::quote!(#warning); +``` + +The output of a [similar example](ui-tests/derive/src/lib.rs) is in [derive_raw.stderr](ui-tests/ui/src/warn/derive_raw.stderr). + ## Used In Substrate uses it to emit warnings for its eDSL (FRAME) on deprecated behaviour. The integration was done in [#13798](https://github.com/paritytech/substrate/pull/13798) and shows how to use these warnings in macro expansion. diff --git a/proc-macro-warning/src/lib.rs b/proc-macro-warning/src/lib.rs index d33c36c..ae93272 100644 --- a/proc-macro-warning/src/lib.rs +++ b/proc-macro-warning/src/lib.rs @@ -233,8 +233,9 @@ impl ToTokens for FormattedWarning { let (name, note, span) = match self { FormattedWarning::Deprecated { name, note, span } => (name, note, span), }; + let span = span.unwrap_or_else(Span::call_site); - let q = quote_spanned!(*span => + let q = quote_spanned!(span => /// This function should not be called and only exists to emit a compiler warning. /// /// It is a No-OP in any case. diff --git a/ui-tests/ui/src/warn/derive_twice_errors.stderr b/ui-tests/ui/src/warn/derive_twice_errors.stderr index eaef3d4..190b49c 100644 --- a/ui-tests/ui/src/warn/derive_twice_errors.stderr +++ b/ui-tests/ui/src/warn/derive_twice_errors.stderr @@ -1,11 +1,28 @@ error[E0428]: the name `test` is defined multiple times - --> src/warn/derive_twice_errors.rs:4:10 + --> src/warn/derive_twice_errors.rs:5:1 | -1 | #[derive(derive::Deprecated)] - | ------------------ previous definition of the value `test` here +2 | struct Test; + | ------------ previous definition of the value `test` here ... -4 | #[derive(derive::Deprecated)] - | ^^^^^^^^^^^^^^^^^^ `test` redefined here +5 | struct Test2; + | ^^^^^^^^^^^^^ `test` redefined here | = note: `test` must be defined only once in the value namespace of this module - = note: this error originates in the derive macro `derive::Deprecated` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: use of deprecated constant `test::_w`: + It is deprecated to foo. + Please instead bar. + --> src/warn/derive_twice_errors.rs:2:1 + | +2 | struct Test; + | ^^^^^^^^^^^^ + | + = note: `-D deprecated` implied by `-D warnings` + +error: use of deprecated constant `test::_w`: + It is deprecated to foo. + Please instead bar. + --> src/warn/derive_twice_errors.rs:5:1 + | +5 | struct Test2; + | ^^^^^^^^^^^^^ From 1b1dba8de1b69c981651165fffddf260c33d7bed Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 13:59:19 +0200 Subject: [PATCH 5/9] fmt Signed-off-by: Oliver Tale-Yazdi --- proc-macro-warning/src/lib.rs | 78 +++++++++++++++++------------------ ui-tests/derive/src/lib.rs | 27 +++++------- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/proc-macro-warning/src/lib.rs b/proc-macro-warning/src/lib.rs index ae93272..b2d511e 100644 --- a/proc-macro-warning/src/lib.rs +++ b/proc-macro-warning/src/lib.rs @@ -13,30 +13,33 @@ mod test; /// Creates a compile-time warning for proc macro use. See [DeprecatedWarningBuilder] for usage. #[derive(Clone)] -pub struct Warning { - pub name: String, - pub index: Option, - pub message: String, - pub links: Vec, - pub span: Span, +pub enum Warning { + /// A *deprecation* warning that notifies users of outdated types and functions. + Deprecated { + name: String, + index: Option, + message: String, + links: Vec, + span: Span, + }, } /// A compile-time warning that was already subject to formatting. -/// +/// /// Any content will be pasted as-is. #[derive(Clone)] pub enum FormattedWarning { /// A *deprecation* warning. Deprecated { /// Unique name of this warning. - /// + /// /// Must be unique in the case that multiple of these warnings are emitted, for example by /// appending a counter. name: syn::Ident, /// The exact note to be used for `note = ""`. note: String, /// The span of the warning. - /// + /// /// Should be set to the original location of where the warning should be emitted. span: Option, }, @@ -69,7 +72,7 @@ impl FormattedWarning { /// .old("my_macro()") /// .new("my_macro::new()") /// .help_link("https:://example.com") -/// // Normally you use the input span, but this is an example: +/// // Normally you use the input span, but this is an example: /// .span(proc_macro2::Span::call_site()) /// .build(); /// @@ -154,7 +157,7 @@ impl DeprecatedWarningBuilder { let new = self.new.ok_or("Missing new")?; let message = format!("It is deprecated to {}.\nPlease instead {}.", old, new); - Ok(Warning { name: title, index: self.index, message, links: self.links, span }) + Ok(Warning::Deprecated { name: title, index: self.index, message, links: self.links, span }) } /// Unwraps [`Self::maybe_build`] for convenience. @@ -165,17 +168,6 @@ impl DeprecatedWarningBuilder { } impl Warning { - /// Create a new *raw* warning. - pub fn new_raw( - name: String, - index: Option, - message: String, - help_links: Vec, - span: Span, - ) -> Warning { - Warning { name, index, message, links: help_links, span } - } - /// Create a new *deprecated* warning. #[must_use] pub fn new_deprecated(title: &str) -> DeprecatedWarningBuilder { @@ -183,18 +175,18 @@ impl Warning { } /// Sanitize the warning message. - fn final_message(&self) -> String { - let lines = self.message.trim().lines().map(|line| line.trim_start()); + fn final_deprecated_message(&self) -> String { + let (message, links) = match self { + Warning::Deprecated { message, links, .. } => (message, links), + }; + + let lines = message.trim().lines().map(|line| line.trim_start()); // Prepend two tabs to each line let message = lines.map(|line| format!("\t\t{}", line)).collect::>().join("\n"); - if !self.links.is_empty() { - let link = self - .links - .iter() - .map(|l| format!("<{}>", l)) - .collect::>() - .join("\n\t\t\t"); + if !links.is_empty() { + let link = + links.iter().map(|l| format!("<{}>", l)).collect::>().join("\n\t\t\t"); format!("\n{}\n\n\t\tFor more info see:\n\t\t\t{}", message, link) } else { format!("\n{}", message) @@ -202,21 +194,27 @@ impl Warning { } /// Sanitize the warning name. - fn final_name(&self) -> syn::Ident { - let name = match self.index { - Some(i) => format!("{}_{}", self.name, i), - None => self.name.clone(), + fn final_deprecated_name(&self) -> syn::Ident { + let (index, name, span) = match self { + Warning::Deprecated { index, name, span, .. } => (*index, name, *span), + }; + + let name = match index { + Some(i) => format!("{}_{}", name, i), + None => name.clone(), }; - syn::Ident::new(&name, self.span) + syn::Ident::new(&name, span) } } impl Into for Warning { fn into(self) -> FormattedWarning { - FormattedWarning::Deprecated { - name: self.final_name(), - note: self.final_message(), - span: Some(self.span), + match self { + Self::Deprecated { span, .. } => FormattedWarning::Deprecated { + name: self.final_deprecated_name(), + note: self.final_deprecated_message(), + span: Some(span), + }, } } } diff --git a/ui-tests/derive/src/lib.rs b/ui-tests/derive/src/lib.rs index 0cf1d22..525c619 100644 --- a/ui-tests/derive/src/lib.rs +++ b/ui-tests/derive/src/lib.rs @@ -1,42 +1,37 @@ //! TESTING ONLY - DO NOT USE. use proc_macro::TokenStream; -use syn::spanned::Spanned; use quote::ToTokens; +use syn::spanned::Spanned; #[proc_macro_derive(Deprecated)] pub fn deprecated(input: TokenStream) -> TokenStream { - impl_dep(input, true) + impl_dep(input, true) } #[proc_macro_derive(DeprecatedNoSpan)] pub fn deprecated2(input: TokenStream) -> TokenStream { - impl_dep(input, false) + impl_dep(input, false) } fn impl_dep(input: TokenStream, span: bool) -> TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let warning = proc_macro_warning::Warning::new_deprecated("test") - .old("foo").new("bar"); - let warning = if span { - warning.span(input.span()) - } else { - warning - }.build(); + let warning = proc_macro_warning::Warning::new_deprecated("test").old("foo").new("bar"); + let warning = if span { warning.span(input.span()) } else { warning }.build(); - warning.into_token_stream().into() + warning.into_token_stream().into() } #[proc_macro_derive(DeprecatedRaw)] pub fn deprecated_raw(input: TokenStream) -> TokenStream { - impl_dep_raw(input) + impl_dep_raw(input) } fn impl_dep_raw(input: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let warning = proc_macro_warning::FormattedWarning::new_deprecated( + let warning = proc_macro_warning::FormattedWarning::new_deprecated( "VeryOldStuff", "\nMy message do noooooooooooooooooooooooooooooot formaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat or chaaaaaaaaaaaaange this, also no line breaks please ;) @@ -44,5 +39,5 @@ other veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy looooooooooooooooooooooong input.span(), ); - warning.into_token_stream().into() + warning.into_token_stream().into() } From fba0fdf8aefb54ae118a1b143ef98759c96627f1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 13:59:33 +0200 Subject: [PATCH 6/9] Bump to v0.4.0 Signed-off-by: Oliver Tale-Yazdi --- proc-macro-warning/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proc-macro-warning/Cargo.toml b/proc-macro-warning/Cargo.toml index 961821d..2d8cde2 100644 --- a/proc-macro-warning/Cargo.toml +++ b/proc-macro-warning/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-warning" -version = "0.3.1" +version = "0.4.0" edition = "2021" license = "GPL-3.0 OR Apache-2.0" authors = ["Oliver Tale-Yazdi "] From 5791b9afd5307212124df7779264ffaed2910c2d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 14:03:47 +0200 Subject: [PATCH 7/9] Update deps and improve CI Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/rust.yml | 5 ++++- proc-macro-warning/Cargo.toml | 4 ++-- ui-tests/derive/Cargo.toml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b574d7a..b1d5089 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,8 +26,11 @@ jobs: - name: Format run: cargo fmt --all -- --check + - name: Check no-std + run: cargo check --no-default-features + - name: Build - run: cargo build --all-targets --all-features # --locked TODO + run: cargo build --all-targets --all-features --locked - name: Test run: TRYBUILD=overwrite cargo test --locked --all-targets --all-features diff --git a/proc-macro-warning/Cargo.toml b/proc-macro-warning/Cargo.toml index 2d8cde2..dc6d547 100644 --- a/proc-macro-warning/Cargo.toml +++ b/proc-macro-warning/Cargo.toml @@ -11,8 +11,8 @@ repository = "https://github.com/ggwpez/proc-macro-warning" [dependencies] proc-macro2 = { version = "1.0.56", default-features = false } -quote = { version = "1.0.26", default-features = false } -syn = { version = "2.0.15", default-features = false } +quote = { version = "1.0.27", default-features = false } +syn = { version = "2.0.16", default-features = false } [dev-dependencies] derive = { path = "../ui-tests/derive" } diff --git a/ui-tests/derive/Cargo.toml b/ui-tests/derive/Cargo.toml index 005f686..33405e8 100644 --- a/ui-tests/derive/Cargo.toml +++ b/ui-tests/derive/Cargo.toml @@ -9,5 +9,5 @@ proc-macro = true [dependencies] proc-macro-warning = { path = "../../proc-macro-warning" } -quote = "1.0.26" -syn = "2.0.15" +quote = "1.0.27" +syn = "2.0.16" From c899f92d961cd2b8a113cc161e642a1293bc9ddb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 14:06:00 +0200 Subject: [PATCH 8/9] Clippy Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/rust.yml | 2 +- proc-macro-warning/src/lib.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b1d5089..33fd491 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: run: cargo fmt --all -- --check - name: Check no-std - run: cargo check --no-default-features + run: cargo check --all-targets --no-default-features --locked - name: Build run: cargo build --all-targets --all-features --locked diff --git a/proc-macro-warning/src/lib.rs b/proc-macro-warning/src/lib.rs index b2d511e..dc6010a 100644 --- a/proc-macro-warning/src/lib.rs +++ b/proc-macro-warning/src/lib.rs @@ -72,7 +72,7 @@ impl FormattedWarning { /// .old("my_macro()") /// .new("my_macro::new()") /// .help_link("https:://example.com") -/// // Normally you use the input span, but this is an example: +/// // Normally you use the input span, but this is an example: /// .span(proc_macro2::Span::call_site()) /// .build(); /// @@ -207,12 +207,12 @@ impl Warning { } } -impl Into for Warning { - fn into(self) -> FormattedWarning { - match self { - Self::Deprecated { span, .. } => FormattedWarning::Deprecated { - name: self.final_deprecated_name(), - note: self.final_deprecated_message(), +impl From for FormattedWarning { + fn from(val: Warning) -> Self { + match val { + Warning::Deprecated { span, .. } => FormattedWarning::Deprecated { + name: val.final_deprecated_name(), + note: val.final_deprecated_message(), span: Some(span), }, } From cd2b0a9460da90afe4ac1702fc86a2f770b265fb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 14 May 2023 14:15:39 +0200 Subject: [PATCH 9/9] Fix CI Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 33fd491..8b7bd22 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: run: cargo fmt --all -- --check - name: Check no-std - run: cargo check --all-targets --no-default-features --locked + run: cargo check --all-targets --no-default-features # still broken --locked - name: Build run: cargo build --all-targets --all-features --locked