Skip to content

Commit

Permalink
Merge pull request #17 from kas-gui/work
Browse files Browse the repository at this point in the history
0.4.1: support GATs
  • Loading branch information
dhardy authored Sep 17, 2022
2 parents 11556d7 + a38a547 commit 454f0dd
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 38 deletions.
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));
}

0 comments on commit 454f0dd

Please sign in to comment.