• 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&logo=docs.rs
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 #![doc(html_root_url = "https://docs.rs/remain/0.2.15")]
147 #![allow(
148     clippy::derive_partial_eq_without_eq,
149     clippy::enum_glob_use,
150     clippy::let_underscore_untyped,
151     clippy::manual_find,
152     clippy::match_same_arms,
153     clippy::module_name_repetitions,
154     clippy::needless_doctest_main,
155     clippy::similar_names
156 )]
157 
158 extern crate proc_macro;
159 
160 mod atom;
161 mod check;
162 mod compare;
163 mod emit;
164 mod format;
165 mod parse;
166 mod visit;
167 
168 use proc_macro::TokenStream;
169 use quote::quote;
170 use syn::parse::Nothing;
171 use syn::{parse_macro_input, ItemFn};
172 
173 use crate::emit::emit;
174 use crate::parse::Input;
175 
176 #[proc_macro_attribute]
sorted(args: TokenStream, input: TokenStream) -> TokenStream177 pub fn sorted(args: TokenStream, input: TokenStream) -> TokenStream {
178     let _ = parse_macro_input!(args as Nothing);
179     let mut input = parse_macro_input!(input as Input);
180     let kind = input.kind();
181 
182     let result = check::sorted(&mut input);
183     let output = TokenStream::from(quote!(#input));
184 
185     match result {
186         Ok(()) => output,
187         Err(err) => emit(&err, kind, output),
188     }
189 }
190 
191 #[proc_macro_attribute]
check(args: TokenStream, input: TokenStream) -> TokenStream192 pub fn check(args: TokenStream, input: TokenStream) -> TokenStream {
193     let _ = parse_macro_input!(args as Nothing);
194     let mut input = parse_macro_input!(input as ItemFn);
195 
196     visit::check(&mut input);
197 
198     TokenStream::from(quote!(#input))
199 }
200