• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! [![github]](https://github.com/dtolnay/remain) [![crates-io]](https://crates.io/crates/remain) [![docs-rs]](https://docs.rs/remain)
2 //!
3 //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
6 //!
7 //! <br>
8 //!
9 //! This crate provides an attribute macro to check at compile time that the
10 //! variants of an enum or the arms of a match expression are written in sorted
11 //! order.
12 //!
13 //! # Syntax
14 //!
15 //! Place a `#[remain::sorted]` attribute on enums, structs, match-expressions,
16 //! or let-statements whose value is a match-expression.
17 //!
18 //! Alternatively, import as `use remain::sorted;` and use `#[sorted]` as the
19 //! attribute.
20 //!
21 //! ```
22 //! # use std::error::Error as StdError;
23 //! # use std::fmt::{self, Display};
24 //! # use std::io;
25 //! #
26 //! #[remain::sorted]
27 //! #[derive(Debug)]
28 //! pub enum Error {
29 //!     BlockSignal(signal::Error),
30 //!     CreateCrasClient(libcras::Error),
31 //!     CreateEventFd(sys_util::Error),
32 //!     CreateSignalFd(sys_util::SignalFdError),
33 //!     CreateSocket(io::Error),
34 //!     DetectImageType(qcow::Error),
35 //!     DeviceJail(io_jail::Error),
36 //!     NetDeviceNew(virtio::NetError),
37 //!     SpawnVcpu(io::Error),
38 //! }
39 //!
40 //! impl Display for Error {
41 //!     # #[remain::check]
42 //!     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 //!         use self::Error::*;
44 //!
45 //!         #[remain::sorted]
46 //!         match self {
47 //!             BlockSignal(e) => write!(f, "failed to block signal: {}", e),
48 //!             CreateCrasClient(e) => write!(f, "failed to create cras client: {}", e),
49 //!             CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e),
50 //!             CreateSignalFd(e) => write!(f, "failed to create signalfd: {}", e),
51 //!             CreateSocket(e) => write!(f, "failed to create socket: {}", e),
52 //!             DetectImageType(e) => write!(f, "failed to detect disk image type: {}", e),
53 //!             DeviceJail(e) => write!(f, "failed to jail device: {}", e),
54 //!             NetDeviceNew(e) => write!(f, "failed to set up virtio networking: {}", e),
55 //!             SpawnVcpu(e) => write!(f, "failed to spawn VCPU thread: {}", e),
56 //!         }
57 //!     }
58 //! }
59 //! #
60 //! # mod signal {
61 //! #     pub use std::io::Error;
62 //! # }
63 //! #
64 //! # mod libcras {
65 //! #     pub use std::io::Error;
66 //! # }
67 //! #
68 //! # mod sys_util {
69 //! #     pub use std::io::{Error, Error as SignalFdError};
70 //! # }
71 //! #
72 //! # mod qcow {
73 //! #     pub use std::io::Error;
74 //! # }
75 //! #
76 //! # mod io_jail {
77 //! #     pub use std::io::Error;
78 //! # }
79 //! #
80 //! # mod virtio {
81 //! #     pub use std::io::Error as NetError;
82 //! # }
83 //! #
84 //! # fn main() {}
85 //! ```
86 //!
87 //! If an enum variant, struct field, or match arm is inserted out of order,\
88 //!
89 //! ```diff
90 //!       NetDeviceNew(virtio::NetError),
91 //!       SpawnVcpu(io::Error),
92 //! +     AaaUhOh(Box<dyn StdError>),
93 //!   }
94 //! ```
95 //!
96 //! then the macro produces a compile error.
97 //!
98 //! ```console
99 //! error: AaaUhOh should sort before BlockSignal
100 //!   --> tests/stable.rs:49:5
101 //!    |
102 //! 49 |     AaaUhOh(Box<dyn StdError>),
103 //!    |     ^^^^^^^
104 //! ```
105 //!
106 //! # Compiler support
107 //!
108 //! The attribute on enums is supported on any rustc version 1.31+.
109 //!
110 //! Rust does not yet have stable support for user-defined attributes within a
111 //! function body, so the attribute on match-expressions and let-statements
112 //! requires a nightly compiler and the following two features enabled:
113 //!
114 //! ```
115 //! # const IGNORE: &str = stringify! {
116 //! #![feature(proc_macro_hygiene, stmt_expr_attributes)]
117 //! # };
118 //! ```
119 //!
120 //! As a stable alternative, this crate provides a function-level attribute
121 //! called `#[remain::check]` which makes match-expression and let-statement
122 //! attributes work on any rustc version 1.31+. Place this attribute on any
123 //! function containing `#[sorted]` to make them work on a stable compiler.
124 //!
125 //! ```
126 //! # use std::fmt::{self, Display};
127 //! #
128 //! # enum Error {}
129 //! #
130 //! impl Display for Error {
131 //!     #[remain::check]
132 //!     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 //!         use self::Error::*;
134 //!
135 //!         #[sorted]
136 //!         match self {
137 //!             /* ... */
138 //!             # _ => unimplemented!(),
139 //!         }
140 //!     }
141 //! }
142 //! #
143 //! # fn main() {}
144 //! ```
145 
146 #![allow(clippy::needless_doctest_main)]
147 
148 extern crate proc_macro;
149 
150 mod atom;
151 mod check;
152 mod compare;
153 mod emit;
154 mod format;
155 mod parse;
156 mod visit;
157 
158 use proc_macro::TokenStream;
159 use quote::quote;
160 use syn::{parse_macro_input, ItemFn};
161 
162 use crate::emit::emit;
163 use crate::parse::{Input, Nothing};
164 
165 #[proc_macro_attribute]
sorted(args: TokenStream, input: TokenStream) -> TokenStream166 pub fn sorted(args: TokenStream, input: TokenStream) -> TokenStream {
167     let _ = parse_macro_input!(args as Nothing);
168     let mut input = parse_macro_input!(input as Input);
169     let kind = input.kind();
170 
171     let result = check::sorted(&mut input);
172     let output = TokenStream::from(quote!(#input));
173 
174     match result {
175         Ok(_) => output,
176         Err(err) => emit(err, kind, output),
177     }
178 }
179 
180 #[proc_macro_attribute]
check(args: TokenStream, input: TokenStream) -> TokenStream181 pub fn check(args: TokenStream, input: TokenStream) -> TokenStream {
182     let _ = parse_macro_input!(args as Nothing);
183     let mut input = parse_macro_input!(input as ItemFn);
184 
185     visit::check(&mut input);
186 
187     TokenStream::from(quote!(#input))
188 }
189