Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.4.1: support GATs #17

Merged
merged 5 commits into from
Sep 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "impl-tools"
version = "0.4.0"
version = "0.4.1"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "MIT/Apache-2.0"
Expand All @@ -27,5 +27,8 @@ path = "lib"
[dev-dependencies]
doc-comment = "0.3.3"

[build-dependencies]
autocfg = "1.1.0"

[workspace]
members = ["lib"]
8 changes: 8 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
extern crate autocfg;

fn main() {
let ac = autocfg::new();
ac.emit_rustc_version(1, 65);

autocfg::rerun_path("build.rs");
}
104 changes: 67 additions & 37 deletions lib/src/for_deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -22,6 +23,27 @@ pub struct ForDeref {
targets: Punctuated<Type, Comma>,
}

// Copied from syn
trait FilterAttrs<'a> {
type Ret: Iterator<Item = &'a Attribute>;

fn outer(self) -> Self::Ret;
}

impl<'a> FilterAttrs<'a> for &'a [Attribute] {
type Ret = iter::Filter<slice::Iter<'a, Attribute>, 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};
Expand Down Expand Up @@ -60,24 +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:
Path {
leading_colon: None,
ref segments,
},
}) if segments.len() == 1
&& matches!(
segments[0].arguments,
PathArguments::None
) =>
{
definitive = Some(segments[0].ident.clone());
if let Type::Path(TypePath { qself: None, path }) =
&pred.bounded_ty
{
if let Some(ident) = path.get_ident() {
definitive = Some(ident.clone());
break;
}
_ => (),
}
}
}
Expand Down Expand Up @@ -108,9 +119,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>(item) {
let item = match syn::parse2::<Item>(item) {
Ok(Item::Trait(item)) => item,
Ok(item) => {
emit_error!(item, "expected trait");
Expand All @@ -137,34 +148,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");
Expand Down
2 changes: 2 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand Down
20 changes: 20 additions & 0 deletions tests/for_deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,23 @@ fn g() {
impls_g(Box::new(S) as Box<dyn G<i32>>);
impls_g(&mut (Box::new(S) as Box<dyn G<i32>>));
}

#[cfg(rustc_1_65)]
#[autoimpl(for<A: trait> Box<A>)]
trait Gat {
type T<X>;
}

#[cfg(rustc_1_65)]
#[test]
fn gat() {
struct S;
impl Gat for S {
type T<X> = X;
}

fn impls_gat(_: impl Gat) {}

impls_gat(S);
impls_gat(Box::new(S));
}