add_syntax/
lib.rs

1/*
2 * Copyright 2022 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of add-syntax.
5 *
6 * add-syntax is licensed under the Apache License, Version 2.0
7 * (the "License"); you may not use add-syntax except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19//! Attribute macros that prepend or append arbitrary syntax. Useful with
20//! [`cfg_attr`].
21//!
22//! This crate provides two attribute macros, [`prepend`] and [`append`], that
23//! add the tokens passed to them to the start or end of the item to which the
24//! attribute is applied, respectively. This is particularly useful with
25//! [`cfg_attr`].
26//!
27//! Example
28//! -------
29//!
30//! Conditionally applying `unsafe` when [`#[may_dangle]`][may_dangle] is used:
31//!
32//! [may_dangle]: https://github.com/rust-lang/rust/issues/34761
33//!
34//! ```rust
35//! # #![cfg_attr(feature = "dropck_eyepatch", feature(dropck_eyepatch))]
36//! # struct Foo<T>(T);
37//! #[cfg_attr(feature = "dropck_eyepatch", add_syntax::prepend(unsafe))]
38//! impl<#[cfg_attr(feature = "dropck_eyepatch", may_dangle)] T> Drop
39//!     for Foo<T>
40//! {
41//!     fn drop(&mut self) { /* ... */ }
42//! }
43//! ```
44//!
45//! If the hypothetical feature `dropck_eyepatch` is enabled, the code above
46//! is equivalent to:
47//!
48//! ```rust
49//! # #![cfg_attr(feature = "dropck_eyepatch", feature(dropck_eyepatch))]
50//! # struct Foo<T>(T);
51//! # #[cfg(feature = "dropck_eyepatch")]
52//! unsafe impl<#[may_dangle] T> Drop for Foo<T> {
53//!     fn drop(&mut self) { /* ... */ }
54//! }
55//! ```
56//!
57//! Otherwise, if the feature is not enabled, the code is equivalent to:
58//!
59//! ```rust
60//! # struct Foo<T>(T);
61//! impl<T> Drop for Foo<T> {
62//!     fn drop(&mut self) { /* ... */ }
63//! }
64//! ```
65//!
66//! [`cfg_attr`]:
67#![doc = "https://doc.rust-lang.org/reference/conditional-compilation.html\
68#the-cfg_attr-attribute"]
69//! [`prepend`]: macro@prepend
70//! [`append`]: macro@append
71
72use proc_macro::{Delimiter, TokenStream, TokenTree};
73
74fn split_attrs(item: TokenStream) -> (TokenStream, TokenStream) {
75    let mut attrs = Vec::<TokenTree>::new();
76    let mut iter = item.into_iter().fuse();
77    loop {
78        use Delimiter::*;
79        use TokenTree::*;
80        match [iter.next(), iter.next()] {
81            [Some(Punct(p)), Some(Group(g))]
82                if (p.as_char(), g.delimiter()) == ('#', Bracket) =>
83            {
84                attrs.extend([p.into(), g.into()]);
85            }
86            mut trees => {
87                let trees = trees.iter_mut().flat_map(Option::take);
88                return (
89                    attrs.into_iter().collect(),
90                    trees.chain(iter).collect(),
91                );
92            }
93        };
94    }
95}
96
97/// Adds the tokens provided to this attribute to the start of the item to
98/// which this attribute is applied.
99#[proc_macro_attribute]
100pub fn prepend(attr: TokenStream, item: TokenStream) -> TokenStream {
101    let (mut item_attrs, rest) = split_attrs(item);
102    item_attrs.extend(attr.into_iter().chain(rest));
103    item_attrs
104}
105
106/// Adds the tokens provided to this attribute to the end of the item to
107/// which this attribute is applied.
108#[proc_macro_attribute]
109pub fn append(attr: TokenStream, mut item: TokenStream) -> TokenStream {
110    item.extend(attr);
111    item
112}