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}