├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE.md
├── README.md
├── src
└── lib.rs
└── tests
├── compile-fail
├── applicability.rs
└── derive.rs
├── run-pass
└── derive.rs
└── tests.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | Cargo.lock
3 | *.rs.bk
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: rust
2 | rust: nightly
3 | after_success: |
4 | [ $TRAVIS_BRANCH = master ] &&
5 | [ $TRAVIS_PULL_REQUEST = false ] &&
6 | cargo doc &&
7 | echo "" > target/doc/index.html &&
8 | sudo pip install ghp-import &&
9 | ghp-import -n target/doc &&
10 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
11 | env:
12 | global:
13 | - secure: YbKKzkQ9+NOvThLMu8toZlcjLvMwV63MO/TyWXkUHQFlXcXgqP2mBZV5w4GhL8FE/uV6MhL50Q0SNgjJ4IJ4xMAkCN2QD4dG/3SKzL6d3D7XEHiVAstuAKbRv7VHFWnLIE7SsispCqhOz4B3A8Dy0RUJtAozGZ8e85p7cLfMkVbCgu3vrvGzsAZtDZd3kB5qQDilcKE1GIG3I9M3xNEUHQS4rX/MKKYSh7KwtCPkOj/iqK8xc7hEcXM9Pa5NKkyWwMiDP9mS2VTInoJfXa3ZhQk7jxWARn7JlNR6iPSmsdIBUsFvV6GDRIL1xaUL/CiT7rqxZIWPw0cKRS+wxgA4W+t8ZoRieQpu1Jv9KJLQWt1C8E9nVsK6m/jBI4CGenc2A/aVsg8MIquGSLce8pCYhD33d9j/XpNMO11PkXTwv4WIvDzs1eiTXhjkRTpBMbAk04LsLWZeG06UkauOPiNkGVC7TkUgv7J6SsDnbuEjGA8FNN5Dth7gI4R81ovGtB5XsbHBx3poQin1rKAZBxGFplAzFtxYK2omlU6x5M1oVMyM5JFVP0J+t/Im5XULIQNbz6AKp3L4GTqoWNjRjqvH9ugWOerUEc/yqxv7DrmsdZWXWC5cDaPFLAmsvzSYkwBuE21/xskahRYoy2ys2Kov6Gb9dVMV892uEavemluXdTo=
14 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We love contributions from everyone.
4 |
5 | By participating in this project, you agree to abide by the Rust [code of conduct](https://www.rust-lang.org/conduct.html).
6 |
7 | We expect everyone to follow the code of conduct
8 | anywhere in our project codebases,
9 | issue trackers, chatrooms, and mailing lists.
10 |
11 | ## Contributing Code
12 |
13 | 1. Fork the repo (preferably on a feature-branch).
14 |
15 | 2. Make your changes.
16 |
17 | 3. Make sure your changes make the tests pass:
18 |
19 | `$ cargo test`
20 |
21 | 4. Make sure your changes make the lints pass:
22 |
23 | `$ cargo clippy` (`$ cargo install clippy`)
24 |
25 | 5. Make sure your changes follow the project's code style. (hint: `$ cargo fmt`)
26 |
27 | 6. Push to your fork.
28 |
29 | 7. Submit a pull request.
30 |
31 | Others will give constructive feedback.
32 | This is a time for discussion and improvements,
33 | and making the necessary changes will be required before we can
34 | merge the contribution.
35 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | authors = ["Vincent Esche "]
3 | categories = ["development-tools"]
4 | description = "A syntax extension providing higher-order attributes to Rust."
5 | documentation = "https://regexident.github.io/apply_attr"
6 | homepage = "https://github.com/regexident/apply_attr"
7 | keywords = ["higher-order", "attribute", "apply"]
8 | license = "MPL-2.0"
9 | name = "apply_attr"
10 | readme = "README.md"
11 | repository = "https://github.com/regexident/apply_attr"
12 | version = "0.2.4"
13 |
14 | [dependencies]
15 | bitflags = "^0.7.0"
16 | compiletest_rs = "0.2.5"
17 |
18 | [dev-dependencies]
19 | compiletest_rs = "0.2.1"
20 |
21 | [lib]
22 | crate-type = ["dylib"]
23 | name = "apply_attr"
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # apply_attr
2 |
3 | [](https://travis-ci.org/regexident/apply_attr)
4 | [](https://crates.io/crates/apply_attr/)
5 | [](https://crates.io/crates/apply_attr/)
6 | [](https://crates.io/crates/apply_attr/)
7 |
8 | ## Synopsis
9 |
10 | A syntax extension providing higher-order attributes to Rust.
11 |
12 | ## Motivation
13 |
14 | Sometimes it would be desirable to be able to apply certain attributes to all items in a scope (`mod`, `trait` or `impl`). The **`apply_attr`** crate aims to provide a versatile API for this.
15 |
16 | Possible use-cases would be:
17 |
18 | - Make all structs in mod `xyz` use `#[derive(PartialEq)]`.
19 | - Mark all methods in a certain `impl` with `#[inline(never)]` (for profiling, e.g.).
20 | - …
21 |
22 | ## Getting Started
23 |
24 | Add the most recent [version](https://crates.io/crates/apply_attr) of `apply_attr`
25 | to your dependencies in your project's `Cargo.toml`.
26 |
27 | Then add …
28 |
29 | ```rust
30 | #![feature(custom_attribute)]
31 |
32 | #![feature(plugin)]
33 | #![plugin(apply_attr)]
34 | ```
35 |
36 | … to your crate's root file (e.g. `lib.rs`, `main.rs`).
37 |
38 | Once that's done you're ready to play!
39 |
40 | # Example
41 |
42 | ```rust
43 | #![feature(custom_attribute)]
44 |
45 | #![feature(plugin)]
46 | #![plugin(apply_attr)]
47 |
48 | // Make all top-level structs as well as those
49 | // within top-level mods implement `PartialEq`:
50 | #![apply_attr(to(structs, mods(structs)), as_default(derive(PartialEq)))]
51 |
52 | pub struct Foo;
53 |
54 | mod Bar {
55 | pub struct Baz;
56 | // ...
57 | }
58 |
59 | // Disable inlining when `no_inline` feature is present:
60 | #[cfg_attr(feature = "no_inline", apply_attr(to(fns), as_override(inline(never))))]
61 | impl Blee {
62 | fn foo(&self) { ... }
63 | fn bar(&self) { ... }
64 | fn baz(&self) { ... }
65 | fn blee(&self) { ... }
66 | }
67 |
68 | fn main() {
69 | Foo == Foo;
70 | Bar::Baz == Bar::Baz;
71 | }
72 | ```
73 |
74 | ## API Reference
75 |
76 | The `apply_attr` syntax extension provides a single higher-order attribute,
77 | conveniently named `apply_attr` expecting two arguments:
78 |
79 | 1. `to(...)` (with `...` being a list of zero or more selectors).
80 | 2. `as_default(...)` or `as_override(...)` (with `...` being a list of zero or more attributes).
81 |
82 | Resulting either of:
83 |
84 | ```rust
85 | #[apply_attr(to(...), as_default(...))]
86 | #[apply_attr(to(...), as_override(...))]
87 | ```
88 |
89 | The first argument (`to(...)`) accepts a nested list of item selectors.
90 |
91 | ## Selectors
92 |
93 | Selectors behave similar to CSS selectors:
94 |
95 | As such a CSS selector like `div > span, p` would translate to `to(div(span), p)`.
96 |
97 | ### Flat Selectors
98 |
99 | The following selectors are supported:
100 |
101 | ```rust
102 | consts
103 | crates
104 | def_impls
105 | enums
106 | fgn_mods
107 | fns
108 | impls
109 | macros
110 | mods
111 | statics
112 | structs
113 | traits
114 | types
115 | uses
116 | ```
117 |
118 | ### Nested Selectors
119 |
120 | With the following ones allowing for nesting:
121 |
122 | ```rust
123 | mods(...)
124 | impls(...)
125 | traits(...)
126 | ```
127 |
128 | Nested selectors denote direct ancestry equivalent to CSS's `outer > inner` path operator.
129 |
130 | ## Attributes
131 |
132 | ### Default
133 |
134 | Attributes can either be applied as using `as_default(...)`, in which case …
135 |
136 | ```rust
137 | #[apply_attr(to(fns), as_default(inline(never)))]
138 | impl Foo {
139 | #[inline(always)]
140 | fn foo() { ... }
141 | }
142 | ```
143 |
144 | … will be turned into …
145 |
146 | ```rust
147 | impl Foo {
148 | #[inline(always)]
149 | fn foo() { ... }
150 | }
151 | ```
152 |
153 | … upon completion.
154 |
155 | ### Overriding
156 |
157 | Or using `as_override(...)`, in which case …
158 |
159 | ```rust
160 | #[apply_attr(to(fns), as_override(inline(never)))]
161 | impl Foo {
162 | #[inline(always)]
163 | fn foo() { ... }
164 | }
165 | ```
166 |
167 | … will be turned into …
168 |
169 | ```rust
170 | impl Foo {
171 | #[inline(never)]
172 | fn foo() { ... }
173 | }
174 | ```
175 |
176 | … upon completion.
177 |
178 | ## Debugging
179 |
180 | To see how the attributes were applied compile your crate using this (requires `nightly`):
181 |
182 | ```bash
183 | cargo rustc -- -Z unstable-options --pretty=expanded
184 | ```
185 |
186 | ## Contributing
187 |
188 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our [code of conduct](https://www.rust-lang.org/conduct.html),
189 | and the process for submitting pull requests to us.
190 |
191 | ## Versioning
192 |
193 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/regexident/apply_attr/tags).
194 |
195 | ## Authors
196 |
197 | * **Vincent Esche** – *Initial work* – [Regexident](https://github.com/Regexident)
198 |
199 | See also the list of [contributors](https://github.com/regexident/apply_attr/contributors) who participated in this project.
200 |
201 | ## License
202 |
203 | This project is licensed under the [**MPL-2.0**](https://www.tldrlegal.com/l/mpl-2.0) – see the [LICENSE.md](LICENSE.md) file for details.
204 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![crate_type="dylib"]
2 | #![feature(plugin_registrar, rustc_private)]
3 | extern crate syntax;
4 | extern crate rustc;
5 |
6 | extern crate rustc_plugin;
7 |
8 | #[macro_use]
9 | extern crate bitflags;
10 |
11 | use rustc_plugin::Registry;
12 |
13 | use syntax::ast;
14 | use syntax::codemap::{Span, Spanned};
15 | use syntax::ext::base::{ExtCtxt, Annotatable};
16 | use syntax::ext::base::SyntaxExtension::MultiModifier;
17 | use syntax::ext::build::AstBuilder;
18 | use syntax::symbol::Symbol;
19 |
20 | #[plugin_registrar]
21 | pub fn plugin_registrar(reg: &mut Registry) {
22 | reg.register_syntax_extension(Symbol::intern("apply_attr"),
23 | MultiModifier(Box::new(expand)));
24 | }
25 |
26 | bitflags! {
27 | flags ItemMask: u32 {
28 | const ITEM_NONE = 0b0,
29 | const ITEM_EXT_CRATE = 0b1 << 0,
30 | const ITEM_USE = 0b1 << 1,
31 | const ITEM_STATIC = 0b1 << 2,
32 | const ITEM_CONST = 0b1 << 3,
33 | const ITEM_FN = 0b1 << 4,
34 | const ITEM_MOD = 0b1 << 5,
35 | const ITEM_FGN_MOD = 0b1 << 6,
36 | const ITEM_GBL_ASM = 0b1 << 7,
37 | const ITEM_TY = 0b1 << 8,
38 | const ITEM_ENUM = 0b1 << 9,
39 | const ITEM_STRUCT = 0b1 << 10,
40 | const ITEM_UNION = 0b1 << 11,
41 | const ITEM_TRAIT = 0b1 << 12,
42 | const ITEM_DEF_IMPL = 0b1 << 13,
43 | const ITEM_IMPL = 0b1 << 14,
44 | const ITEM_MAC = 0b1 << 15,
45 | const ITEM_MAC_DEF = 0b1 << 16,
46 | }
47 | }
48 |
49 | type Selector = ast::MetaItem;
50 |
51 | trait SelectorValidation {
52 | fn validate(&self, ctx: &mut ExtCtxt) -> bool;
53 | }
54 |
55 | impl SelectorValidation for Selector {
56 | fn validate(&self, ctx: &mut ExtCtxt) -> bool {
57 | let name = &*(self.name.as_str());
58 | match self.node {
59 | ast::MetaItemKind::List(ref sub_selectors) => {
60 | let valid_item = match name {
61 | "mods" | "traits" | "impls" => true,
62 | _ => {
63 | let error_msg = format!("Unrecognized selector `{}(...)`.", name);
64 | ctx.span_err(self.span, &error_msg);
65 | false
66 | }
67 | };
68 | let valid_sub_selectors = sub_selectors.iter()
69 | .fold(true, |valid, sub_selector| {
70 | match sub_selector.node {
71 | ast::NestedMetaItemKind::MetaItem(ref item) => {
72 | valid & item.validate(ctx)
73 | }
74 | ast::NestedMetaItemKind::Literal(ref _lit) => false,
75 | }
76 | });
77 | valid_item & valid_sub_selectors
78 | }
79 | ast::MetaItemKind::Word => {
80 | match name {
81 | "crates" | "uses" | "statics" | "consts" | "fns" | "mods" | "fgn_mods" |
82 | "types" | "enums" | "structs" | "unions" | "traits" | "def_impls" |
83 | "impls" | "macros" => true,
84 | _ => {
85 | let error_msg = format!("Unrecognized selector `{}`.", name);
86 | ctx.span_err(self.span, &error_msg);
87 | false
88 | }
89 | }
90 | }
91 | ast::MetaItemKind::NameValue(ref _value) => {
92 | let valid_item = false;
93 | let error_msg = format!("Unexpected name value pair `{} = ...`.", name);
94 | ctx.span_err(self.span, &error_msg);
95 | valid_item
96 | }
97 | }
98 | }
99 | }
100 |
101 | enum Attributes {
102 | Default(Vec),
103 | Override(Vec),
104 | }
105 |
106 | impl Attributes {
107 | fn augment(&self, existing: &[ast::Attribute]) -> Vec {
108 | let mut expanded_attrs = vec![];
109 | match *self {
110 | Attributes::Default(ref attrs) => {
111 | expanded_attrs.extend(attrs.iter().cloned());
112 | expanded_attrs.extend(existing.iter().cloned());
113 | }
114 | Attributes::Override(ref attrs) => {
115 | expanded_attrs.extend(existing.iter().cloned());
116 | expanded_attrs.extend(attrs.iter().cloned());
117 | }
118 | }
119 | expanded_attrs
120 | }
121 | }
122 |
123 | fn expand(ctx: &mut ExtCtxt, span: Span, meta: &ast::MetaItem, ann: Annotatable) -> Annotatable {
124 | if let Some((selectors, attributes)) = extract_meta(ctx, meta) {
125 | fn not_applicable(ctx: &mut ExtCtxt, span: Span) {
126 | ctx.span_err(span, "Only applicable to `mod`, `trait` or `impl` items.");
127 | }
128 | match ann {
129 | Annotatable::Item(item_ptr) => {
130 | let ptr = item_ptr.map(|item| {
131 | match item.node {
132 | ast::ItemKind::Mod(..) |
133 | ast::ItemKind::Trait(..) |
134 | ast::ItemKind::Impl(..) => {}
135 | _ => {
136 | not_applicable(ctx, span);
137 | }
138 | }
139 | expand_item(ctx, item, &selectors[..], &attributes, true)
140 | });
141 | Annotatable::Item(ptr)
142 | }
143 | Annotatable::TraitItem(item_ptr) => {
144 | not_applicable(ctx, span);
145 | let ptr =
146 | item_ptr.map(|item| expand_trait_item(ctx, item, &selectors[..], &attributes, true));
147 | Annotatable::TraitItem(ptr)
148 | }
149 | Annotatable::ImplItem(item_ptr) => {
150 | not_applicable(ctx, span);
151 | let ptr =
152 | item_ptr.map(|item| expand_impl_item(ctx, item, &selectors[..], &attributes, true));
153 | Annotatable::ImplItem(ptr)
154 | }
155 | }
156 | } else {
157 | ann
158 | }
159 | }
160 |
161 | fn expand_item(ctx: &mut ExtCtxt,
162 | item: ast::Item,
163 | selectors: &[&Selector],
164 | attributes: &Attributes,
165 | is_root: bool)
166 | -> ast::Item {
167 | let item_mask = map_item_to_mask(&item);
168 | let selector_mask = extract_mask_from_selectors(selectors);
169 | let sub_selectors = if is_root {
170 | selectors.to_owned() // simply forward selectors for root item
171 | } else {
172 | extract_sub_selectors(&selectors[..], item_mask)
173 | };
174 | let augmented_attributes = fold_attributes(item_mask, selector_mask, &item.attrs, attributes);
175 | let node = match item.node {
176 | ast::ItemKind::Mod(m) => {
177 | let expanded_items = m.items
178 | .into_iter()
179 | .map(|item_ptr| {
180 | item_ptr.map(|item| expand_item(ctx, item, &sub_selectors, attributes, false))
181 | });
182 | ast::ItemKind::Mod(ast::Mod {
183 | inner: m.inner,
184 | items: expanded_items.collect(),
185 | })
186 | }
187 | ast::ItemKind::Trait(unsafety, generics, bounds, items) => {
188 | let expanded_items = items.into_iter()
189 | .map(|item| expand_trait_item(ctx, item, &sub_selectors, attributes, false));
190 | ast::ItemKind::Trait(unsafety, generics, bounds, expanded_items.collect())
191 | }
192 | ast::ItemKind::Impl(unsafety, polarity, defaultness, generics, trt, typ, items) => {
193 | let expanded_items = items.into_iter()
194 | .map(|item| expand_impl_item(ctx, item, &sub_selectors, attributes, false));
195 | ast::ItemKind::Impl(unsafety,
196 | polarity,
197 | defaultness,
198 | generics,
199 | trt,
200 | typ,
201 | expanded_items.collect())
202 | }
203 | _ => item.node,
204 | };
205 | ast::Item {
206 | node: node,
207 | attrs: augmented_attributes,
208 | ..item
209 | }
210 | }
211 |
212 | fn expand_trait_item(_ctx: &mut ExtCtxt,
213 | item: ast::TraitItem,
214 | selectors: &[&Selector],
215 | attributes: &Attributes,
216 | _is_root: bool)
217 | -> ast::TraitItem {
218 | let item_mask = map_trait_item_to_mask(&item);
219 | let selector_mask = extract_mask_from_selectors(selectors);
220 | let augmented_attributes =
221 | fold_attributes(item_mask, selector_mask, &item.attrs[..], attributes);
222 | ast::TraitItem { attrs: augmented_attributes, ..item }
223 | }
224 |
225 | fn expand_impl_item(_ctx: &mut ExtCtxt,
226 | item: ast::ImplItem,
227 | selectors: &[&Selector],
228 | attributes: &Attributes,
229 | _is_root: bool)
230 | -> ast::ImplItem {
231 | let item_mask = map_impl_item_to_mask(&item);
232 | let selector_mask = extract_mask_from_selectors(selectors);
233 | let augmented_attributes = fold_attributes(item_mask, selector_mask, &item.attrs, attributes);
234 | ast::ImplItem { attrs: augmented_attributes, ..item }
235 | }
236 |
237 | fn extract_mask_from_selectors(selectors: &[&Selector]) -> ItemMask {
238 | selectors.iter().fold(ITEM_NONE, |mask, selector| {
239 | let name = &*((*selector).name.as_str());
240 | mask |
241 | match (*selector).node {
242 | ast::MetaItemKind::Word => map_selector_to_mask(name),
243 | _ => ITEM_NONE,
244 | }
245 | })
246 | }
247 |
248 | fn extract_sub_selectors<'a>(selectors: &[&'a Selector], mask: ItemMask) -> Vec<&'a Selector> {
249 | for selector in selectors {
250 | if let ast::MetaItemKind::List(ref vec) = (*selector).node {
251 | let name = &*((*selector).name.as_str());
252 | if mask & map_selector_to_mask(name) != ITEM_NONE {
253 | return vec.iter()
254 | .filter_map(|sub_selector| {
255 | match sub_selector.node {
256 | ast::NestedMetaItemKind::MetaItem(ref item) => Some(item),
257 | ast::NestedMetaItemKind::Literal(ref _lit) => None,
258 | }
259 | })
260 | .collect();
261 | }
262 | }
263 | }
264 | vec![]
265 | }
266 |
267 | fn extract_meta<'a>(ctx: &mut ExtCtxt,
268 | meta: &'a ast::MetaItem)
269 | -> Option<(Vec<&'a Selector>, Attributes)> {
270 | if let ast::MetaItemKind::List(ref vec) = meta.node {
271 | // TODO: Migrate to slice patterns, once stabilized:
272 | if vec.len() == 2 {
273 | let selectors = extract_selectors(ctx, &vec[0]);
274 | let attributes = extract_attributes(ctx, &vec[1]);
275 | if let (Some(selectors), Some(attributes)) = (selectors, attributes) {
276 | return Some((selectors, attributes));
277 | }
278 | }
279 | }
280 | ctx.span_err(meta.span,
281 | "Expected 'apply_attr(to(...), as_default|as_override(...))'.");
282 | None
283 | }
284 |
285 | fn fold_attributes(item_mask: ItemMask,
286 | selector_mask: ItemMask,
287 | item_attributes: &[ast::Attribute],
288 | attributes: &Attributes)
289 | -> Vec {
290 | if (selector_mask & item_mask) != ITEM_NONE {
291 | attributes.augment(item_attributes)
292 | } else {
293 | item_attributes.to_owned()
294 | }
295 | }
296 |
297 | fn extract_selectors<'a>(ctx: &mut ExtCtxt,
298 | meta: &'a Spanned)
299 | -> Option> {
300 | if let ast::NestedMetaItemKind::MetaItem(ref item) = meta.node {
301 | if let ast::MetaItemKind::List(ref selectors) = item.node {
302 | if meta.check_name("to") {
303 | let selector_items: Vec<_> = selectors.iter()
304 | .filter_map(|selector| {
305 | match selector.node {
306 | ast::NestedMetaItemKind::MetaItem(ref item) => Some(item),
307 | ast::NestedMetaItemKind::Literal(_) => None,
308 | }
309 | })
310 | .collect();
311 | if selector_items.iter().all(|item| item.validate(ctx)) {
312 | return Some(selector_items);
313 | } else {
314 | return None;
315 | }
316 | }
317 | }
318 | }
319 | ctx.span_err(meta.span, "Expected `to(...)`.");
320 | None
321 | }
322 |
323 | fn extract_attributes(ctx: &mut ExtCtxt,
324 | meta: &Spanned)
325 | -> Option {
326 | if let ast::NestedMetaItemKind::MetaItem(ref item) = meta.node {
327 | if let ast::MetaItemKind::List(ref vec) = item.node {
328 | let attributes = vec.iter().filter_map(|meta| {
329 | if let ast::NestedMetaItemKind::MetaItem(ref item) = meta.node {
330 | Some(ctx.attribute(meta.span, item.clone()))
331 | } else {
332 | None
333 | }
334 | });
335 | if meta.check_name("as_default") {
336 | return Some(Attributes::Default(attributes.collect()));
337 | } else if meta.check_name("as_override") {
338 | return Some(Attributes::Override(attributes.collect()));
339 | }
340 | }
341 | }
342 | ctx.span_err(meta.span,
343 | "Expected `as_default(...)` or `as_override(...)`.");
344 | None
345 | }
346 |
347 | fn map_selector_to_mask(selector: &str) -> ItemMask {
348 | match selector {
349 | "crates" => ITEM_EXT_CRATE,
350 | "uses" => ITEM_USE,
351 | "statics" => ITEM_STATIC,
352 | "consts" => ITEM_CONST,
353 | "fns" => ITEM_FN,
354 | "mods" => ITEM_MOD,
355 | "fgn_mods" => ITEM_FGN_MOD,
356 | "global_asms" => ITEM_GBL_ASM,
357 | "types" => ITEM_TY,
358 | "enums" => ITEM_ENUM,
359 | "structs" => ITEM_STRUCT,
360 | "unions" => ITEM_UNION,
361 | "traits" => ITEM_TRAIT,
362 | "def_impls" => ITEM_DEF_IMPL,
363 | "impls" => ITEM_IMPL,
364 | "macros" => ITEM_MAC,
365 | "macro_defs" => ITEM_MAC_DEF,
366 | _ => ITEM_NONE,
367 | }
368 | }
369 |
370 | fn map_item_to_mask(item: &ast::Item) -> ItemMask {
371 | match item.node {
372 | ast::ItemKind::ExternCrate(..) => ITEM_EXT_CRATE,
373 | ast::ItemKind::Use(..) => ITEM_USE,
374 | ast::ItemKind::Static(..) => ITEM_STATIC,
375 | ast::ItemKind::Const(..) => ITEM_CONST,
376 | ast::ItemKind::Fn(..) => ITEM_FN,
377 | ast::ItemKind::Mod(..) => ITEM_MOD,
378 | ast::ItemKind::ForeignMod(..) => ITEM_FGN_MOD,
379 | ast::ItemKind::GlobalAsm(..) => ITEM_GBL_ASM,
380 | ast::ItemKind::Ty(..) => ITEM_TY,
381 | ast::ItemKind::Enum(..) => ITEM_ENUM,
382 | ast::ItemKind::Struct(..) => ITEM_STRUCT,
383 | ast::ItemKind::Union(..) => ITEM_UNION,
384 | ast::ItemKind::Trait(..) => ITEM_TRAIT,
385 | ast::ItemKind::DefaultImpl(..) => ITEM_DEF_IMPL,
386 | ast::ItemKind::Impl(..) => ITEM_IMPL,
387 | ast::ItemKind::Mac(..) => ITEM_MAC,
388 | ast::ItemKind::MacroDef(..) => ITEM_MAC_DEF,
389 | }
390 | }
391 |
392 | fn map_trait_item_to_mask(item: &ast::TraitItem) -> ItemMask {
393 | match item.node {
394 | ast::TraitItemKind::Const(..) => ITEM_CONST,
395 | ast::TraitItemKind::Method(..) => ITEM_FN,
396 | ast::TraitItemKind::Type(..) => ITEM_TY,
397 | ast::TraitItemKind::Macro(..) => ITEM_MAC,
398 | }
399 | }
400 |
401 | fn map_impl_item_to_mask(item: &ast::ImplItem) -> ItemMask {
402 | match item.node {
403 | ast::ImplItemKind::Const(..) => ITEM_CONST,
404 | ast::ImplItemKind::Method(..) => ITEM_FN,
405 | ast::ImplItemKind::Type(..) => ITEM_TY,
406 | ast::ImplItemKind::Macro(..) => ITEM_MAC,
407 | }
408 | }
409 |
--------------------------------------------------------------------------------
/tests/compile-fail/applicability.rs:
--------------------------------------------------------------------------------
1 | #![feature(custom_attribute)]
2 |
3 | #![feature(plugin)]
4 | #![plugin(apply_attr)]
5 |
6 | // make compiletest accept dummy attribute `foo`:
7 | #![feature(custom_attribute)]
8 |
9 | #[apply_attr(to(), as_default())]
10 | pub mod foo {}
11 |
12 | #[apply_attr(to(), as_default())]
13 | pub trait Bar {}
14 |
15 | pub struct Baz;
16 | #[apply_attr(to(), as_default())]
17 | impl Baz {}
18 |
19 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
20 | extern crate apply_attr;
21 |
22 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
23 | use bar;
24 |
25 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
26 | static FOO: usize = 42;
27 |
28 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
29 | const BAR: usize = 42;
30 |
31 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
32 | fn foo_bar() {}
33 |
34 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
35 | type Foo = Bar;
36 |
37 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
38 | enum FooBar {}
39 |
40 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
41 | struct BazBlee {}
42 |
43 | // fgn_mod
44 | // def_impls
45 |
46 | #[apply_attr(to(), as_default())] //~ ERROR Only applicable to `mod`, `trait` or `impl` items.
47 | macro_rules! foo_bar_baz {
48 | () => ()
49 | }
50 |
51 | fn main() {}
52 |
--------------------------------------------------------------------------------
/tests/compile-fail/derive.rs:
--------------------------------------------------------------------------------
1 | #![feature(custom_attribute)]
2 |
3 | #![feature(plugin)]
4 | #![plugin(apply_attr)]
5 |
6 | #![apply_attr(to(structs), as_default(derive(PartialEq)))]
7 |
8 | pub enum Foo {
9 | Bar
10 | }
11 |
12 | pub struct Bar;
13 |
14 | #[apply_attr(to(enums), as_override(derive(PartialEq)))]
15 | mod foo {
16 | pub struct Baz;
17 | pub enum Blee {
18 | FooBar
19 | }
20 | }
21 |
22 | fn main() {
23 | Foo::Bar == Foo::Bar;
24 | //~^ ERROR binary operation `==` cannot be applied to type `Foo`
25 |
26 | Bar == Bar;
27 |
28 | foo::Baz == foo::Baz;
29 | //~^ ERROR binary operation `==` cannot be applied to type `foo::Baz`
30 |
31 | foo::Blee::FooBar == foo::Blee::FooBar;
32 | }
33 |
--------------------------------------------------------------------------------
/tests/run-pass/derive.rs:
--------------------------------------------------------------------------------
1 | #![feature(custom_attribute)]
2 |
3 | #![feature(plugin)]
4 | #![plugin(apply_attr)]
5 |
6 | #![apply_attr(to(enums, structs, mods(structs)), as_default(derive(PartialEq)))]
7 |
8 | struct Foo;
9 |
10 | enum Bar {
11 | Baz
12 | }
13 |
14 | mod blee {
15 | pub struct Blee;
16 | }
17 |
18 | fn main() {
19 | Foo == Foo;
20 | Bar::Baz == Bar::Baz;
21 | blee::Blee == blee::Blee;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/tests.rs:
--------------------------------------------------------------------------------
1 | extern crate compiletest_rs as compiletest;
2 |
3 | use std::path::PathBuf;
4 |
5 | fn run_mode(mode: &'static str) {
6 | let mut config = compiletest::default_config();
7 | let cfg_mode = mode.parse().ok().expect("Invalid mode");
8 |
9 | config.mode = cfg_mode;
10 | config.src_base = PathBuf::from(format!("tests/{}", mode));
11 | config.target_rustcflags = Some("-L target/debug -L target/debug/deps".to_string());
12 |
13 | compiletest::run_tests(&config);
14 | }
15 |
16 | #[test]
17 | fn compile_test() {
18 | run_mode("compile-fail");
19 | run_mode("run-pass");
20 | }
21 |
--------------------------------------------------------------------------------