diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 120fbcd..9514721 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ jobs: - os: ubuntu-latest toolchain: nightly variant: docs + features: "--features nightly-diagnostics" - os: macos-latest toolchain: beta - os: windows-latest @@ -41,13 +42,13 @@ jobs: - name: Build docs if: ${{ matrix.variant == 'docs' }} - run: cargo doc --all-features --all --no-deps + run: cargo doc ${{ matrix.features }} --all --no-deps - name: Test impl-tools-lib - run: cargo test --manifest-path lib/Cargo.toml --all-features ${{ matrix.targets }} + run: cargo test --manifest-path lib/Cargo.toml ${{ matrix.targets }} - name: Test impl-tools - run: cargo test --all-features ${{ matrix.targets }} + run: cargo test ${{ matrix.features }} ${{ matrix.targets }} - name: Test test-cfg working-directory: tests/test-cfg diff --git a/Cargo.toml b/Cargo.toml index 6975aab..255a4d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,12 @@ edition.workspace = true [lib] proc-macro = true +[features] +# Enables better proc-macro diagnostics (including warnings); nightly only. +nightly-diagnostics = ["proc-macro-error2/nightly"] +# Squelch lint: "autoimpl on trait that has a method with Self: Sized bound" +allow-trait-autoimpl-with-sized-fn-bound = ["impl-tools-lib/allow-trait-autoimpl-with-sized-fn-bound"] + [dependencies.proc-macro-error2] version = "2.0" default-features = false diff --git a/compile-errors/expect-self.rs b/compile-errors/common/expect-self.rs similarity index 100% rename from compile-errors/expect-self.rs rename to compile-errors/common/expect-self.rs diff --git a/compile-errors/expect-self.stderr b/compile-errors/common/expect-self.stderr similarity index 69% rename from compile-errors/expect-self.stderr rename to compile-errors/common/expect-self.stderr index 9f45c93..4b5ea3e 100644 --- a/compile-errors/expect-self.stderr +++ b/compile-errors/common/expect-self.stderr @@ -1,5 +1,5 @@ error: expected `Self` or `A` or `A<...>` or `Trait for Self`, etc - --> compile-errors/expect-self.rs:5:10 + --> compile-errors/common/expect-self.rs:5:10 | 5 | impl () { | ^^ diff --git a/compile-errors/wrong-item-type.rs b/compile-errors/common/wrong-item-type.rs similarity index 100% rename from compile-errors/wrong-item-type.rs rename to compile-errors/common/wrong-item-type.rs diff --git a/compile-errors/wrong-item-type.stderr b/compile-errors/common/wrong-item-type.stderr similarity index 85% rename from compile-errors/wrong-item-type.stderr rename to compile-errors/common/wrong-item-type.stderr index 1628239..9782eee 100644 --- a/compile-errors/wrong-item-type.stderr +++ b/compile-errors/common/wrong-item-type.stderr @@ -1,5 +1,5 @@ error: #[autoimpl(Trait)] can only be used on enum or struct items - --> compile-errors/wrong-item-type.rs:3:1 + --> compile-errors/common/wrong-item-type.rs:3:1 | 3 | #[impl_tools::autoimpl(Copy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/compile-errors/expect-self-anon.rs b/compile-errors/nightly/expect-self-anon.rs similarity index 100% rename from compile-errors/expect-self-anon.rs rename to compile-errors/nightly/expect-self-anon.rs diff --git a/compile-errors/nightly/expect-self-anon.stderr b/compile-errors/nightly/expect-self-anon.stderr new file mode 100644 index 0000000..e37518c --- /dev/null +++ b/compile-errors/nightly/expect-self-anon.stderr @@ -0,0 +1,16 @@ +error: expected `Self` or `Trait for Self` + --> compile-errors/nightly/expect-self-anon.rs:6:14 + | +6 | impl () { + | ^^ + +error: expected expression, found end of macro arguments + --> compile-errors/nightly/expect-self-anon.rs:2:13 + | + 2 | let _ = impl_tools::impl_anon! { + | _____________^ + 3 | | #[derive(Clone, Debug)] + 4 | | struct (i32 = 123); +... | +11 | | }; + | |_____^ diff --git a/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs b/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs new file mode 100644 index 0000000..71fe8fb --- /dev/null +++ b/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs @@ -0,0 +1,21 @@ +use impl_tools::autoimpl; +use core::fmt::Debug; + +#[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] +trait G +where + V: Debug, +{ + fn g(&self) -> V; + + fn s(&self, f: impl Fn(V) -> X) -> X + where + Self: Sized, + { + f(self.g()) + } +} + +fn main() { + compile_error!("Warnings verification"); +} diff --git a/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.stderr b/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.stderr new file mode 100644 index 0000000..faacb50 --- /dev/null +++ b/compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.stderr @@ -0,0 +1,22 @@ +warning: autoimpl on trait that has a method with Self: Sized bound + --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:4:1 + | + 4 | #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: method impl uses default implementation, not deref + --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:11:5 + | +11 | / fn s(&self, f: impl Fn(V) -> X) -> X +12 | | where +13 | | Self: Sized, +... | +16 | | } + | |_____^ + = note: this warning originates in the attribute macro `autoimpl` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: Warnings verification + --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:20:5 + | +20 | compile_error!("Warnings verification"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/compile-errors/stable/expect-self-anon.rs b/compile-errors/stable/expect-self-anon.rs new file mode 100644 index 0000000..75b482e --- /dev/null +++ b/compile-errors/stable/expect-self-anon.rs @@ -0,0 +1,12 @@ +fn main() { + let _ = impl_tools::impl_anon! { + #[derive(Clone, Debug)] + struct (i32 = 123); + + impl () { + fn get(&self) -> i32 { + self.0 + } + } + }; +} diff --git a/compile-errors/expect-self-anon.stderr b/compile-errors/stable/expect-self-anon.stderr similarity index 63% rename from compile-errors/expect-self-anon.stderr rename to compile-errors/stable/expect-self-anon.stderr index 6d0a56f..1db79b4 100644 --- a/compile-errors/expect-self-anon.stderr +++ b/compile-errors/stable/expect-self-anon.stderr @@ -1,5 +1,5 @@ error: expected `Self` or `Trait for Self` - --> compile-errors/expect-self-anon.rs:6:14 + --> compile-errors/stable/expect-self-anon.rs:6:14 | 6 | impl () { | ^^ diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 6474c6d..c2a0d9d 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,6 +11,10 @@ repository.workspace = true rust-version.workspace = true edition.workspace = true +[features] +# Squelch lint: "autoimpl on trait that has a method with Self: Sized bound" +allow-trait-autoimpl-with-sized-fn-bound = [] + [dependencies] quote = "1.0" proc-macro2 = "1.0" diff --git a/lib/src/autoimpl/for_deref.rs b/lib/src/autoimpl/for_deref.rs index aa23647..9896dd4 100644 --- a/lib/src/autoimpl/for_deref.rs +++ b/lib/src/autoimpl/for_deref.rs @@ -7,7 +7,7 @@ use crate::generics::{GenericParam, Generics, TypeParamBound, WherePredicate}; use proc_macro2::{Span, TokenStream}; -use proc_macro_error2::{emit_call_site_error, emit_error}; +use proc_macro_error2::{emit_call_site_error, emit_call_site_warning, emit_error}; use quote::{quote, ToTokens, TokenStreamExt}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; @@ -198,6 +198,12 @@ impl ForDeref { "cannot autoimpl trait with Deref"; note = item.span() => "method has a bound on Self and no default implementation"; ); + } else if !cfg!(feature = "allow-trait-autoimpl-with-sized-fn-bound") { + // TODO(rust proc_macro_lint): this should be a configurable lint + emit_call_site_warning!( + "autoimpl on trait that has a method with Self: Sized bound"; + note = item.span() => "method impl uses default implementation, not deref"; + ); } continue; diff --git a/src/lib.rs b/src/lib.rs index 416d448..06d332b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,10 +240,12 @@ pub fn impl_default(args: TokenStream, item: TokenStream) -> TokenStream { /// Traits using generics and trait items using generics are, for the most part, /// supported. /// -/// Items with a where clause with a type bound on `Self` are not supported -/// since the item is not guaranteed to exist on the definitive type. -/// Exception: methods with a default implementation (in this case the item is -/// skipped). +/// Items with a where clause with a type bound on `Self` cannot be implemented +/// via [`Deref`] since the item is not guaranteed to exist on the definitive +/// type. Such items with a default implementation may be implemented using this +/// that default implementation, though this results in a warning by default +/// (requires feature "nightly-diagnostics"). +/// In other cases an error is reported. /// /// An example: /// ``` diff --git a/tests/compile-errors.rs b/tests/compile-errors.rs index b8a6ba1..5fbbf88 100644 --- a/tests/compile-errors.rs +++ b/tests/compile-errors.rs @@ -1,5 +1,11 @@ #[test] fn compile_errors() { let t = trybuild::TestCases::new(); - t.compile_fail("compile-errors/*.rs"); + t.compile_fail("compile-errors/common/*.rs"); + + #[cfg(feature = "nightly-diagnostics")] + t.compile_fail("compile-errors/nightly/*.rs"); + + #[cfg(not(feature = "nightly-diagnostics"))] + t.compile_fail("compile-errors/stable/*.rs"); } diff --git a/tests/for_deref.rs b/tests/for_deref.rs index f2b9cfc..616effe 100644 --- a/tests/for_deref.rs +++ b/tests/for_deref.rs @@ -67,13 +67,6 @@ where V: Debug, { fn g(&self) -> V; - - fn s(&self, f: impl Fn(V) -> X) -> X - where - Self: Sized, - { - f(self.g()) - } } #[test] @@ -87,7 +80,6 @@ fn g() { fn impls_g(g: impl G) { assert_eq!(g.g(), 123); - assert!(g.s(|x| x == 123)); } impls_g(S);