Skip to content

Commit 38b36d7

Browse files
committed
Support trait-only and impl-only attributes
1 parent 8b284ea commit 38b36d7

File tree

3 files changed

+122
-20
lines changed

3 files changed

+122
-20
lines changed

src/ast.rs

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,17 @@ pub(crate) struct Attribute {
140140

141141
#[derive(Clone, Copy, PartialEq)]
142142
pub(crate) enum AttributeKind {
143-
// #[doc ...]
144-
Doc,
145-
// #[inline ...]
146-
Inline,
147-
Other,
143+
TraitOnly,
144+
ImplOnly,
145+
TraitAndImpl,
148146
}
149147

150148
impl Attribute {
151149
pub(crate) fn new(tokens: Vec<TokenTree>) -> Self {
152150
Self {
153151
pound_token: Punct::new('#', Spacing::Alone),
154152
tokens: Group::new(Delimiter::Bracket, tokens.into_iter().collect()),
155-
kind: AttributeKind::Other,
153+
kind: AttributeKind::TraitAndImpl,
156154
}
157155
}
158156
}
@@ -311,15 +309,19 @@ pub(crate) struct TraitItemType {
311309
pub(crate) mod parsing {
312310
use std::iter::FromIterator;
313311

314-
use proc_macro::{Delimiter, Punct, Spacing, TokenStream, TokenTree};
312+
use proc_macro::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree};
315313

316314
use super::{
317315
Attribute, AttributeKind, BoundLifetimes, ConstParam, FnArg, GenericParam, Generics,
318316
ImplItem, ImplItemConst, ImplItemMethod, ImplItemType, ItemImpl, Lifetime, LifetimeDef,
319317
PredicateLifetime, PredicateType, Signature, TypeParam, TypeParamBound, Visibility,
320318
WhereClause, WherePredicate,
321319
};
322-
use crate::{error::Result, iter::TokenIter, to_tokens::ToTokens};
320+
use crate::{
321+
error::{Error, Result},
322+
iter::TokenIter,
323+
to_tokens::ToTokens,
324+
};
323325

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

389391
fn parse_attrs(input: &mut TokenIter) -> Result<Vec<Attribute>> {
390392
let mut attrs = vec![];
393+
let mut prev_kind_override: Option<(AttributeKind, Group)> = None;
391394
while input.peek_t(&'#') {
392395
let pound_token = input.parse_punct('#')?;
393396
let tokens = input.parse_group(Delimiter::Bracket)?;
394-
let mut kind = AttributeKind::Other;
397+
let mut kind = AttributeKind::TraitAndImpl;
398+
let mut cur_kind_override = None;
395399
let mut iter = TokenIter::new(tokens.stream());
396400
if let Some(TokenTree::Ident(i)) = iter.next() {
397401
match iter.next() {
398402
// ignore #[path ...]
399403
Some(TokenTree::Punct(ref p)) if p.as_char() == ':' => {}
400-
_ => match &*i.to_string() {
401-
"doc" => kind = AttributeKind::Doc,
402-
"inline" => kind = AttributeKind::Inline,
404+
next => match &*i.to_string() {
405+
"doc" => kind = AttributeKind::TraitOnly,
406+
"inline" => kind = AttributeKind::ImplOnly,
407+
"ext_attr" => match next.map(|x| x.to_string()).as_deref() {
408+
Some("(impl_only)") => {
409+
cur_kind_override = Some(AttributeKind::ImplOnly);
410+
}
411+
Some("(trait_only)") => {
412+
cur_kind_override = Some(AttributeKind::TraitOnly);
413+
}
414+
Some(_) => {
415+
return Err(Error::new(&tokens, "invalid attribute tag".into()))
416+
}
417+
None => {
418+
return Err(Error::new(&tokens, "missing attribute tag".into()))
419+
}
420+
},
403421
_ => {}
404422
},
405423
}
406424
}
407425

426+
match (prev_kind_override.take(), cur_kind_override) {
427+
(Some((prev_kind_override, _)), None) => {
428+
kind = prev_kind_override;
429+
}
430+
(None, Some(cur_kind_override)) => {
431+
prev_kind_override = Some((cur_kind_override, tokens));
432+
continue;
433+
}
434+
(Some(_), Some(_)) => {
435+
return Err(Error::new(&tokens, "repeated attribute tag".into()))
436+
}
437+
(None, None) => {}
438+
}
439+
408440
let attr = Attribute { pound_token, tokens, kind };
409441
attrs.push(attr);
410442
}
411-
Ok(attrs)
443+
444+
if let Some((_, tokens)) = prev_kind_override {
445+
Err(Error::new(&tokens, "unused attribute tag".into()))
446+
} else {
447+
Ok(attrs)
448+
}
412449
}
413450

414451
fn parse_generics(input: &mut TokenIter) -> Result<Generics> {

src/lib.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,8 @@ fn trait_from_impl(item: &mut ItemImpl, trait_name: Ident) -> Result<ItemTrait>
457457
})?;
458458

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

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

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

554557
let mut attrs = impl_method.attrs.clone();
555-
find_remove(&mut impl_method.attrs, AttributeKind::Doc); // https://github.com/taiki-e/easy-ext/issues/20
556-
find_remove(&mut attrs, AttributeKind::Inline); // `#[inline]` is ignored on function prototypes
558+
find_remove(&mut impl_method.attrs, AttributeKind::TraitOnly);
559+
find_remove(&mut attrs, AttributeKind::ImplOnly);
557560
Ok(TraitItem::Method(TraitItemMethod {
558561
attrs,
559562
sig: {

tests/test.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,3 +666,65 @@ fn arbitrary_self_types() {
666666
Rc::new(String::default()).recv_rc_mut();
667667
Box::pin(String::default()).recv_pin_box();
668668
}
669+
670+
#[test]
671+
fn impl_only_attrs() {
672+
#![deny(unused_variables)]
673+
#![allow(non_camel_case_types)]
674+
675+
#[ext(E1)]
676+
#[ext_attr(impl_only)]
677+
#[allow(unused_variables)]
678+
impl str {
679+
fn foo(&self) {
680+
let x = 0;
681+
}
682+
}
683+
684+
#[ext(E2)]
685+
impl str {
686+
#[ext_attr(impl_only)]
687+
#[allow(unused_variables)]
688+
fn foo(&self) {
689+
let x = 0;
690+
}
691+
}
692+
693+
#[ext(not_camel_case)]
694+
#[ext_attr(impl_only)]
695+
#[deny(non_camel_case_types)]
696+
impl str {
697+
fn foo(&self) {}
698+
}
699+
}
700+
701+
#[test]
702+
fn trait_only_attrs() {
703+
#![allow(unused_variables)]
704+
#![deny(non_camel_case_types)]
705+
706+
#[ext(E1)]
707+
#[ext_attr(trait_only)]
708+
#[deny(unused_variables)]
709+
impl str {
710+
fn foo(&self) {
711+
let x = 0;
712+
}
713+
}
714+
715+
#[ext(E2)]
716+
impl str {
717+
#[ext_attr(trait_only)]
718+
#[deny(unused_variables)]
719+
fn foo(&self) {
720+
let x = 0;
721+
}
722+
}
723+
724+
#[ext(not_camel_case)]
725+
#[ext_attr(trait_only)]
726+
#[allow(non_camel_case_types)]
727+
impl str {
728+
fn foo(&self) {}
729+
}
730+
}

0 commit comments

Comments
 (0)