From 3c06877f270f22c3a9f0ec76083a97bd9f44fb9d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 17 Sep 2022 10:39:11 +0100 Subject: [PATCH 1/5] Revise for_deref and fix for GATs --- lib/src/for_deref.rs | 105 ++++++++++++++++++++++++++++--------------- tests/for_deref.rs | 18 ++++++++ 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/lib/src/for_deref.rs b/lib/src/for_deref.rs index c64068a..84fb346 100644 --- a/lib/src/for_deref.rs +++ b/lib/src/for_deref.rs @@ -11,9 +11,10 @@ use crate::generics::{ use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; use quote::{quote, ToTokens, TokenStreamExt}; +use std::{iter, slice}; use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::{parse2, FnArg, Ident, Item, Path, PathArguments, Token, TraitItem, Type, TypePath}; +use syn::token::{Colon2, Comma, Eq}; +use syn::{Attribute, FnArg, Ident, Item, Token, TraitItem, Type, TypePath}; /// Autoimpl for types supporting `Deref` pub struct ForDeref { @@ -22,6 +23,27 @@ pub struct ForDeref { targets: Punctuated, } +// Copied from syn +trait FilterAttrs<'a> { + type Ret: Iterator; + + fn outer(self) -> Self::Ret; +} + +impl<'a> FilterAttrs<'a> for &'a [Attribute] { + type Ret = iter::Filter, fn(&&Attribute) -> bool>; + + fn outer(self) -> Self::Ret { + fn is_outer(attr: &&Attribute) -> bool { + match attr.style { + syn::AttrStyle::Outer => true, + syn::AttrStyle::Inner(_) => false, + } + } + self.iter().filter(is_outer) + } +} + mod parsing { use super::*; use syn::parse::{Error, Parse, ParseStream, Result}; @@ -60,22 +82,12 @@ mod parsing { if let WherePredicate::Type(pred) = pred { for bound in &pred.bounds { if matches!(bound, TypeParamBound::TraitSubst(_)) { - match pred.bounded_ty { - Type::Path(TypePath { - qself: None, - path: - Path { - leading_colon: None, - ref segments, - }, - }) if segments.len() == 1 - && matches!( - segments[0].arguments, - PathArguments::None - ) => - { - definitive = Some(segments[0].ident.clone()); - break; + match &pred.bounded_ty { + Type::Path(TypePath { qself: None, path }) => { + if let Some(ident) = path.get_ident() { + definitive = Some(ident.clone()); + break; + } } _ => (), } @@ -108,9 +120,9 @@ impl ForDeref { /// Expand over the given `item` /// /// This attribute does not modify the item. - /// The caller should append the result to `item` tokens. + /// The caller should append the result to `item` impl_items. pub fn expand(self, item: TokenStream) -> TokenStream { - let item = match parse2::(item) { + let item = match syn::parse2::(item) { Ok(Item::Trait(item)) => item, Ok(item) => { emit_error!(item, "expected trait"); @@ -137,34 +149,53 @@ impl ForDeref { let mut toks = TokenStream::new(); for target in self.targets { + // Tokenize, like ToTokens impls for syn::TraitItem*, but for definition let mut impl_items = TokenStream::new(); + let tokens = &mut impl_items; for item in &item.items { match item { TraitItem::Const(item) => { - let ident = &item.ident; - let ty = &item.ty; - impl_items.append_all(quote! { - const #ident : #ty = #definitive :: #ident; - }); + tokens.append_all(item.attrs.outer()); + item.const_token.to_tokens(tokens); + item.ident.to_tokens(tokens); + item.colon_token.to_tokens(tokens); + item.ty.to_tokens(tokens); + + Eq::default().to_tokens(tokens); + definitive.to_tokens(tokens); + Colon2::default().to_tokens(tokens); + item.ident.to_tokens(tokens); + + item.semi_token.to_tokens(tokens); } TraitItem::Method(item) => { - let sig = &item.sig; - let ident = &sig.ident; - let params = sig.inputs.iter().map(|arg| match arg { + tokens.append_all(item.attrs.outer()); + item.sig.to_tokens(tokens); + + let ident = &item.sig.ident; + let params = item.sig.inputs.iter().map(|arg| match arg { FnArg::Receiver(arg) => &arg.self_token as &dyn ToTokens, FnArg::Typed(arg) => &arg.pat, }); - impl_items.append_all(quote! { - #sig { - #definitive :: #ident ( #(#params),* ) - } - }); + tokens.append_all(quote! { { + #definitive :: #ident ( #(#params),* ) + } }); } TraitItem::Type(item) => { - let ident = &item.ident; - impl_items.append_all(quote! { - type #ident = #definitive :: #ident; - }); + tokens.append_all(item.attrs.outer()); + item.type_token.to_tokens(tokens); + item.ident.to_tokens(tokens); + + let (_, ty_generics, _) = item.generics.split_for_impl(); + ty_generics.to_tokens(tokens); + + Eq::default().to_tokens(tokens); + definitive.to_tokens(tokens); + Colon2::default().to_tokens(tokens); + item.ident.to_tokens(tokens); + ty_generics.to_tokens(tokens); + + item.semi_token.to_tokens(tokens); } TraitItem::Macro(item) => { emit_error!(item, "unsupported: macro item in trait"); diff --git a/tests/for_deref.rs b/tests/for_deref.rs index 9cdd777..d2e0b7f 100644 --- a/tests/for_deref.rs +++ b/tests/for_deref.rs @@ -71,3 +71,21 @@ fn g() { impls_g(Box::new(S) as Box>); impls_g(&mut (Box::new(S) as Box>)); } + +#[autoimpl(for Box)] +trait Gat { + type T; +} + +#[test] +fn gat() { + struct S; + impl Gat for S { + type T = X; + } + + fn impls_gat(_: impl Gat) {} + + impls_gat(S); + impls_gat(Box::new(S)); +} From 68afeb2245fd7f9b4aa0b255d0bcd275a690ee4f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 17 Sep 2022 10:47:07 +0100 Subject: [PATCH 2/5] Use autocfg to limit GAT test to rustc >= 1.65 --- Cargo.toml | 3 +++ build.rs | 8 ++++++++ tests/for_deref.rs | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 51ac28b..0098ff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,8 @@ path = "lib" [dev-dependencies] doc-comment = "0.3.3" +[build-dependencies] +autocfg = "1.1.0" + [workspace] members = ["lib"] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..45d8db6 --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +extern crate autocfg; + +fn main() { + let ac = autocfg::new(); + ac.emit_rustc_version(1, 65); + + autocfg::rerun_path("build.rs"); +} diff --git a/tests/for_deref.rs b/tests/for_deref.rs index d2e0b7f..6b5fc07 100644 --- a/tests/for_deref.rs +++ b/tests/for_deref.rs @@ -72,11 +72,13 @@ fn g() { impls_g(&mut (Box::new(S) as Box>)); } +#[cfg(rustc_1_65)] #[autoimpl(for Box)] trait Gat { type T; } +#[cfg(rustc_1_65)] #[test] fn gat() { struct S; From 66bf5fb7ecc61c150857b3651a0a41b49b9a50c5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 17 Sep 2022 10:48:13 +0100 Subject: [PATCH 3/5] CI: test beta on MacOSX --- .github/workflows/test.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69adde2..86e95e3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,6 +34,27 @@ jobs: - name: Clippy run: cargo clippy --all + beta: + name: Beta on MacOS + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: beta + override: true + components: clippy + + - name: Test impl-tools-lib + run: cargo test --manifest-path lib/Cargo.toml --all-features + - name: Test impl-tools + run: cargo test --all-features + - name: Clippy (beta) + run: cargo clippy --all -- -D warnings -A unknown_lints + stable: name: Stable on Windows runs-on: windows-latest From d76e7173e1a237695a2d2d92b9f4c7532fbd1a00 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 17 Sep 2022 10:54:08 +0100 Subject: [PATCH 4/5] Prepare 0.4.1 --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbc481..f762c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.1] — 2022-09-17 + +- Fix `#[autoimpl]` on traits for GATs and attributes on trait const/method/type items (#17) + ## [0.4.0] — 2022-08-19 Change signature of `ScopeAttr::apply`: replace `args: TokenStream, attr_span: Span` diff --git a/Cargo.toml b/Cargo.toml index 0098ff2..ea8cb90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impl-tools" -version = "0.4.0" +version = "0.4.1" authors = ["Diggory Hardy "] edition = "2021" license = "MIT/Apache-2.0" From a38a5479ef6c36634aacdc29e704d18718bbf86e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 17 Sep 2022 10:59:22 +0100 Subject: [PATCH 5/5] Clippy fixes --- lib/src/for_deref.rs | 13 ++++++------- lib/src/lib.rs | 2 ++ src/lib.rs | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/src/for_deref.rs b/lib/src/for_deref.rs index 84fb346..6e8697d 100644 --- a/lib/src/for_deref.rs +++ b/lib/src/for_deref.rs @@ -82,14 +82,13 @@ mod parsing { if let WherePredicate::Type(pred) = pred { for bound in &pred.bounds { if matches!(bound, TypeParamBound::TraitSubst(_)) { - match &pred.bounded_ty { - Type::Path(TypePath { qself: None, path }) => { - if let Some(ident) = path.get_ident() { - definitive = Some(ident.clone()); - break; - } + if let Type::Path(TypePath { qself: None, path }) = + &pred.bounded_ty + { + if let Some(ident) = path.get_ident() { + definitive = Some(ident.clone()); + break; } - _ => (), } } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 0169f71..fdb169b 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -10,6 +10,8 @@ //! merely documentation plus wrappers around this crate. #![deny(missing_docs)] +// Lint advocates use of bool::then_some, stablizied in rustc 1.62.0 +#![allow(clippy::unnecessary_lazy_evaluations)] pub mod autoimpl; mod default; diff --git a/src/lib.rs b/src/lib.rs index 8c83319..7ea629b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ // https://www.apache.org/licenses/LICENSE-2.0 #![allow(clippy::needless_doctest_main)] +// Lint advocates use of bool::then_some, stablizied in rustc 1.62.0 +#![allow(clippy::unnecessary_lazy_evaluations)] //! # Impl-tools //!