Skip to content
Open
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
69 changes: 56 additions & 13 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,17 @@ pub(crate) struct Attribute {

#[derive(Clone, Copy, PartialEq)]
pub(crate) enum AttributeKind {
// #[doc ...]
Doc,
// #[inline ...]
Inline,
Other,
TraitOnly,
ImplOnly,
TraitAndImpl,
}

impl Attribute {
pub(crate) fn new(tokens: Vec<TokenTree>) -> Self {
Self {
pound_token: Punct::new('#', Spacing::Alone),
tokens: Group::new(Delimiter::Bracket, tokens.into_iter().collect()),
kind: AttributeKind::Other,
kind: AttributeKind::TraitAndImpl,
}
}
}
Expand Down Expand Up @@ -311,15 +309,19 @@ pub(crate) struct TraitItemType {
pub(crate) mod parsing {
use std::iter::FromIterator;

use proc_macro::{Delimiter, Punct, Spacing, TokenStream, TokenTree};
use proc_macro::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree};

use super::{
Attribute, AttributeKind, BoundLifetimes, ConstParam, FnArg, GenericParam, Generics,
ImplItem, ImplItemConst, ImplItemMethod, ImplItemType, ItemImpl, Lifetime, LifetimeDef,
PredicateLifetime, PredicateType, Signature, TypeParam, TypeParamBound, Visibility,
WhereClause, WherePredicate,
};
use crate::{error::Result, iter::TokenIter, to_tokens::ToTokens};
use crate::{
error::{Error, Result},
iter::TokenIter,
to_tokens::ToTokens,
};

fn parse_until_punct(input: &mut TokenIter, ch: char) -> Result<(Vec<TokenTree>, Punct)> {
let mut buf = vec![];
Expand Down Expand Up @@ -388,27 +390,68 @@ pub(crate) mod parsing {

fn parse_attrs(input: &mut TokenIter) -> Result<Vec<Attribute>> {
let mut attrs = vec![];
let mut prev_kind_override: Option<(AttributeKind, Group)> = None;
while input.peek_t(&'#') {
let pound_token = input.parse_punct('#')?;
let tokens = input.parse_group(Delimiter::Bracket)?;
let mut kind = AttributeKind::Other;
let mut kind = AttributeKind::TraitAndImpl;
let mut cur_kind_override = None;
let mut iter = TokenIter::new(tokens.stream());
if let Some(TokenTree::Ident(i)) = iter.next() {
match iter.next() {
// ignore #[path ...]
Some(TokenTree::Punct(ref p)) if p.as_char() == ':' => {}
_ => match &*i.to_string() {
"doc" => kind = AttributeKind::Doc,
"inline" => kind = AttributeKind::Inline,
next => match &*i.to_string() {
"doc" => kind = AttributeKind::TraitOnly,
"inline" => kind = AttributeKind::ImplOnly,
"ext_attr" => {
if let Some(next) = next {
match &*next.to_string() {
"(impl_only)" => {
cur_kind_override = Some(AttributeKind::ImplOnly);
}
"(trait_only)" => {
cur_kind_override = Some(AttributeKind::TraitOnly);
}
_ => {
return Err(Error::new(
&tokens,
"invalid attribute tag".into(),
))
}
}
} else {
return Err(Error::new(&tokens, "missing attribute tag".into()));
}
}
_ => {}
},
}
}

match (prev_kind_override.take(), cur_kind_override) {
(Some((prev_kind_override, _)), None) => {
kind = prev_kind_override;
}
(None, Some(cur_kind_override)) => {
prev_kind_override = Some((cur_kind_override, tokens));
continue;
}
(Some(_), Some(_)) => {
return Err(Error::new(&tokens, "repeated attribute tag".into()))
}
(None, None) => {}
}

let attr = Attribute { pound_token, tokens, kind };
attrs.push(attr);
}
Ok(attrs)

if let Some((_, tokens)) = prev_kind_override {
Err(Error::new(&tokens, "unused attribute tag".into()))
} else {
Ok(attrs)
}
}

fn parse_generics(input: &mut TokenIter) -> Result<Generics> {
Expand Down
17 changes: 10 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ fn trait_from_impl(item: &mut ItemImpl, trait_name: Ident) -> Result<ItemTrait>
})?;

let mut attrs = item.attrs.clone();
find_remove(&mut item.attrs, AttributeKind::Doc); // https://github.com/taiki-e/easy-ext/issues/20
find_remove(&mut item.attrs, AttributeKind::TraitOnly);
find_remove(&mut attrs, AttributeKind::ImplOnly);
attrs.push(Attribute::new(vec![
TokenTree::Ident(Ident::new("allow", Span::call_site())),
TokenTree::Group(Group::new(
Expand Down Expand Up @@ -522,8 +523,9 @@ fn trait_item_from_impl_item(
let vis = mem::replace(&mut impl_const.vis, Visibility::Inherited);
check_visibility(vis, prev_vis, impl_vis, &impl_const.ident)?;

let attrs = impl_const.attrs.clone();
find_remove(&mut impl_const.attrs, AttributeKind::Doc); // https://github.com/taiki-e/easy-ext/issues/20
let mut attrs = impl_const.attrs.clone();
find_remove(&mut impl_const.attrs, AttributeKind::TraitOnly);
find_remove(&mut attrs, AttributeKind::ImplOnly);
Ok(TraitItem::Const(TraitItemConst {
attrs,
const_token: impl_const.const_token.clone(),
Expand All @@ -537,8 +539,9 @@ fn trait_item_from_impl_item(
let vis = mem::replace(&mut impl_type.vis, Visibility::Inherited);
check_visibility(vis, prev_vis, impl_vis, &impl_type.ident)?;

let attrs = impl_type.attrs.clone();
find_remove(&mut impl_type.attrs, AttributeKind::Doc); // https://github.com/taiki-e/easy-ext/issues/20
let mut attrs = impl_type.attrs.clone();
find_remove(&mut impl_type.attrs, AttributeKind::TraitOnly);
find_remove(&mut attrs, AttributeKind::ImplOnly);
Ok(TraitItem::Type(TraitItemType {
attrs,
type_token: impl_type.type_token.clone(),
Expand All @@ -552,8 +555,8 @@ fn trait_item_from_impl_item(
check_visibility(vis, prev_vis, impl_vis, &impl_method.sig.ident)?;

let mut attrs = impl_method.attrs.clone();
find_remove(&mut impl_method.attrs, AttributeKind::Doc); // https://github.com/taiki-e/easy-ext/issues/20
find_remove(&mut attrs, AttributeKind::Inline); // `#[inline]` is ignored on function prototypes
find_remove(&mut impl_method.attrs, AttributeKind::TraitOnly);
find_remove(&mut attrs, AttributeKind::ImplOnly);
Ok(TraitItem::Method(TraitItemMethod {
attrs,
sig: {
Expand Down
62 changes: 62 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,3 +666,65 @@ fn arbitrary_self_types() {
Rc::new(String::default()).recv_rc_mut();
Box::pin(String::default()).recv_pin_box();
}

#[test]
fn impl_only_attrs() {
#![deny(unused_variables)]
#![allow(non_camel_case_types)]

#[ext(E1)]
#[ext_attr(impl_only)]
#[allow(unused_variables)]
impl str {
fn foo(&self) {
let x = 0;
}
}

#[ext(E2)]
impl str {
#[ext_attr(impl_only)]
#[allow(unused_variables)]
fn foo(&self) {
let x = 0;
}
}

#[ext(not_camel_case)]
#[ext_attr(impl_only)]
#[deny(non_camel_case_types)]
impl str {
fn foo(&self) {}
}
}

#[test]
fn trait_only_attrs() {
#![allow(unused_variables)]
#![deny(non_camel_case_types)]

#[ext(E1)]
#[ext_attr(trait_only)]
#[deny(unused_variables)]
impl str {
fn foo(&self) {
let x = 0;
}
}

#[ext(E2)]
impl str {
#[ext_attr(trait_only)]
#[deny(unused_variables)]
fn foo(&self) {
let x = 0;
}
}

#[ext(not_camel_case)]
#[ext_attr(trait_only)]
#[allow(non_camel_case_types)]
impl str {
fn foo(&self) {}
}
}