Skip to content

Commit

Permalink
Merge pull request #5 from popzxc/support-doc-comments
Browse files Browse the repository at this point in the history
Support doc comments
  • Loading branch information
popzxc authored Feb 20, 2022
2 parents f03c0f5 + 6a78496 commit 2d16219
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Version 0.x.y (202x-xx-xx)

## Version 0.3.0 (2022-02-20)

- Added support for doc-comments.

## Version 0.2.0 (2021-05-25)

- Support for trait bounds on generic params for the alias was added.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "trait-set"
version = "0.2.0"
version = "0.3.0"
authors = ["Igor Aleksanov <[email protected]>"]
edition = "2018"
repository = "https://github.com/popzxc/trait-set"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ as
[visibility] trait [AliasName][<generics>] = [Element1] + [Element2] + ... + [ElementN];
```

[`trait_set`]: https://docs.rs/trait-set/latest/trait_set/macro.trait_set.html
[alias]: https://doc.rust-lang.org/unstable-book/language-features/trait-alias.html
[tracking_issue]: https://github.com/rust-lang/rust/issues/41517

Expand All @@ -46,6 +47,7 @@ use trait_set::trait_set;

trait_set! {
// Simple combination of two traits.
/// Doc-comments are also supported btw.
pub trait ThreadSafe = Send + Sync;

// Generic alias that gets passed to the associated type.
Expand Down
37 changes: 36 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ use syn::{
parse_macro_input,
punctuated::Punctuated,
spanned::Spanned,
GenericParam, Generics, Ident, Result, Token, TypeTraitObject, Visibility,
Attribute, GenericParam, Generics, Ident, Lit, Meta, MetaNameValue, Result, Token,
TypeTraitObject, Visibility,
};

/// Represents one trait alias.
struct TraitSet {
doc_comment: Option<String>,
visibility: Visibility,
_trait_token: Token![trait],
alias_name: Ident,
Expand All @@ -53,6 +55,32 @@ struct TraitSet {
}

impl TraitSet {
/// Attempts to parse doc-comments from the trait attributes
/// and returns the results as a single string.
/// If multiple doc-comments were provided (e.g. with `///` and `#[doc]`),
/// they will be joined with a newline.
fn parse_doc(attrs: &[Attribute]) -> Result<Option<String>> {
let mut out = String::new();

for attr in attrs {
// Check whether current attribute is `#[doc = "..."]`.
if let Meta::NameValue(MetaNameValue { path, lit, .. }) = attr.parse_meta()? {
if let Some(path_ident) = path.get_ident() {
if path_ident == "doc" {
if let Lit::Str(doc_comment) = lit {
out += &doc_comment.value();
// Newlines are not included in the literal value,
// so we have to add them manually.
out.push('\n');
}
}
}
}
}

Ok(if !out.is_empty() { Some(out) } else { None })
}

/// Renders trait alias into a new trait with bounds set.
fn render(self) -> TokenStream2 {
// Generic and non-generic implementation have slightly different
Expand All @@ -70,7 +98,9 @@ impl TraitSet {
let visibility = self.visibility;
let alias_name = self.alias_name;
let bounds = self.traits.bounds;
let doc_comment = self.doc_comment.map(|val| quote! { #[doc = #val] });
quote! {
#doc_comment
#visibility trait #alias_name: #bounds {}

impl<_INNER> #alias_name for _INNER where _INNER: #bounds {}
Expand All @@ -82,6 +112,7 @@ impl TraitSet {
let visibility = self.visibility;
let alias_name = self.alias_name;
let bounds = self.traits.bounds;
let doc_comment = self.doc_comment.map(|val| quote! { #[doc = #val] });

// We differentiate `generics` and `bound_generics` because in the
// `impl<X> Trait<Y>` block there must be no trait bounds in the `<Y>` part,
Expand All @@ -102,6 +133,7 @@ impl TraitSet {
// generics, because generics can contain lifetimes, and lifetimes
// should always go first.
quote! {
#doc_comment
#visibility trait #alias_name<#bound_generics>: #bounds {}

impl<#bound_generics, _INNER> #alias_name<#unbound_generics> for _INNER where _INNER: #bounds {}
Expand All @@ -111,7 +143,9 @@ impl TraitSet {

impl Parse for TraitSet {
fn parse(input: ParseStream) -> Result<Self> {
let attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
let result = TraitSet {
doc_comment: Self::parse_doc(&attrs)?,
visibility: input.parse()?,
_trait_token: input.parse()?,
alias_name: input.parse()?,
Expand Down Expand Up @@ -157,6 +191,7 @@ impl ManyTraitSet {
/// use trait_set::trait_set;
///
/// trait_set! {
/// /// Doc-comments are also supported btw.
/// pub trait ThreadSafe = Send + Sync;
/// pub trait ThreadSafeIterator<T> = ThreadSafe + Iterator<Item = T>;
/// pub trait ThreadSafeBytesIterator = ThreadSafeIterator<u8>;
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/correct/10_doc_comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! Tests that adding doc-comment doesn't break the build.
use trait_set::trait_set;

trait_set! {
/// This is a doc-comment!
///
/// It has multiple lines!
///
#[doc = "And it's mixed with different flavors of comments..."]
/** Even block-comments, */
pub(crate) trait TraitSet = Send + Sync;
}

fn test_set<T: TraitSet>(_arg: T) {}

fn main() {
test_set(10u8);
test_set("hello");
}
22 changes: 17 additions & 5 deletions tests/ui/incorrect/01_bound_failure.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
error[E0277]: `RefCell<u8>` cannot be shared between threads safely
--> $DIR/01_bound_failure.rs:13:10
|
10 | fn test<T: ThreadSafe>(_t: T) {}
| ---------- required by this bound in `test`
...
13 | test(RefCell::new(10u8));
| ^^^^^^^^^^^^^^^^^^ `RefCell<u8>` cannot be shared between threads safely
| ---- ^^^^^^^^^^^^^^^^^^ `RefCell<u8>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `RefCell<u8>`
= note: required because of the requirements on the impl of `ThreadSafe` for `RefCell<u8>`
note: required because of the requirements on the impl of `ThreadSafe` for `RefCell<u8>`
--> $DIR/01_bound_failure.rs:6:1
|
6 | / trait_set! {
7 | | pub trait ThreadSafe = Send + Sync;
| | ^^^^^^^^^^
8 | | }
| |_^
note: required by a bound in `test`
--> $DIR/01_bound_failure.rs:10:12
|
10 | fn test<T: ThreadSafe>(_t: T) {}
| ^^^^^^^^^^ required by this bound in `test`
= note: this error originates in the macro `trait_set` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit 2d16219

Please sign in to comment.