aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Banks <_@jeremy.ca>2019-05-11 10:52:18 -0400
committerJeremy Banks <_@jeremy.ca>2019-05-11 11:07:51 -0400
commitc41add741dbe4a97fb04d5236ba91f87ca567b4e (patch)
tree5bc1079fa5173d1a4ece44f78119cc99f64f1e85
parent320efb2ae98e815be196609d7ba600a78cf6727f (diff)
downloadremain-c41add741dbe4a97fb04d5236ba91f87ca567b4e.tar.gz
Add support for sorting named struct fields.
-rw-r--r--Cargo.toml2
-rw-r--r--README.md18
-rw-r--r--src/check.rs17
-rw-r--r--src/emit.rs5
-rw-r--r--src/lib.rs6
-rw-r--r--src/parse.rs14
-rw-r--r--tests/stable.rs10
-rw-r--r--tests/ui/struct.rs11
-rw-r--r--tests/ui/struct.stderr5
-rw-r--r--tests/ui/unsupported.rs6
-rw-r--r--tests/ui/unsupported.stderr12
-rw-r--r--tests/unstable.rs9
12 files changed, 102 insertions, 13 deletions
diff --git a/Cargo.toml b/Cargo.toml
index fc097ab..a928b27 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.2" # remember to update number in readme for major versions
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
-description = "Compile-time checks that an enum or match is written in sorted order."
+description = "Compile-time checks that an enum, struct, or match is written in sorted order."
repository = "https://github.com/dtolnay/remain"
documentation = "https://docs.rs/remain"
readme = "README.md"
diff --git a/README.md b/README.md
index fe1851a..3af2e72 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,8 @@ remain = "0.1"
## Syntax
-Place a `#[remain::sorted]` attribute on enums, on match-expressions, or on
-let-statements whose value is a match-expression.
+Place a `#[remain::sorted]` attribute on enums, on named structs, on
+match-expressions, or on let-statements whose value is a match-expression.
Alternatively, import as `use remain::sorted;` and use `#[sorted]` as the
attribute.
@@ -37,6 +37,16 @@ pub enum Error {
SpawnVcpu(io::Error),
}
+#[remain::sorted]
+#[derive(Debug)]
+pub enum Registers {
+ ax: u16,
+ cx: u16,
+ di: u16,
+ si: u16,
+ sp: u16,
+}
+
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
@@ -57,7 +67,7 @@ impl Display for Error {
}
```
-If an enum variant or match arm is inserted out of order,
+If an enum variant, struct field, or match arm is inserted out of order,
```diff
NetDeviceNew(virtio::NetError),
@@ -78,7 +88,7 @@ error: AaaUhOh should sort before BlockSignal
## Compiler support
-The attribute on enums is supported on any rustc version 1.31+.
+The attribute on enums and structs is supported on any rustc version 1.31+.
Rust does not yet have stable support for user-defined attributes within a
function body, so the attribute on match-expressions and let-statements requires
diff --git a/src/check.rs b/src/check.rs
index 281c052..1b50442 100644
--- a/src/check.rs
+++ b/src/check.rs
@@ -1,5 +1,5 @@
use syn::{Arm, Ident, Result, Variant};
-use syn::{Error, Pat, PatIdent};
+use syn::{Error, Field, Fields, Pat, PatIdent};
use crate::compare::Path;
use crate::format;
@@ -8,6 +8,13 @@ use crate::parse::Input::{self, *};
pub fn sorted(input: Input) -> Result<()> {
let paths = match input {
Enum(item) => collect_paths(item.variants)?,
+ Struct(item) => {
+ if let Fields::Named(fields) = item.fields {
+ collect_paths(fields.named)?
+ } else {
+ unreachable!("must be named field")
+ }
+ }
Match(expr) | Let(expr) => collect_paths(expr.arms)?,
};
@@ -44,6 +51,14 @@ impl IntoPath for Variant {
}
}
+impl IntoPath for Field {
+ fn into_path(self) -> Result<Path> {
+ Ok(Path {
+ segments: vec![self.ident.expect("must be named field")],
+ })
+ }
+}
+
impl IntoPath for Arm {
fn into_path(self) -> Result<Path> {
// Sort by just the first pat.
diff --git a/src/emit.rs b/src/emit.rs
index b98ad1d..d1ddda8 100644
--- a/src/emit.rs
+++ b/src/emit.rs
@@ -7,6 +7,7 @@ use syn::Error;
pub enum Kind {
Enum,
Match,
+ Struct,
Let,
}
@@ -21,7 +22,7 @@ pub fn emit(err: Error, kind: Kind, original: TokenStream) -> TokenStream {
let original = proc_macro2::TokenStream::from(original);
let expanded = match kind {
- Kind::Enum | Kind::Let => quote!(#err #original),
+ Kind::Enum | Kind::Let | Kind::Struct => quote!(#err #original),
Kind::Match => quote!({ #err #original }),
};
@@ -32,7 +33,7 @@ pub fn emit(err: Error, kind: Kind, original: TokenStream) -> TokenStream {
// https://github.com/rust-lang/rust/issues/43081
fn probably_has_spans(kind: Kind) -> bool {
match kind {
- Kind::Enum => true,
+ Kind::Enum | Kind::Struct => true,
Kind::Match | Kind::Let => false,
}
}
diff --git a/src/lib.rs b/src/lib.rs
index dc1ab0b..4034303 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,8 +4,8 @@
//!
//! # Syntax
//!
-//! Place a `#[remain::sorted]` attribute on enums, on match-expressions, or on
-//! let-statements whose value is a match-expression.
+//! Place a `#[remain::sorted]` attribute on enums, on named structs, on
+//! match-expressions, or on let-statements whose value is a match-expression.
//!
//! Alternatively, import as `use remain::sorted;` and use `#[sorted]` as the
//! attribute.
@@ -76,7 +76,7 @@
//! # fn main() {}
//! ```
//!
-//! If an enum variant or match arm is inserted out of order,
+//! If an enum variant, struct field, or match arm is inserted out of order,\
//!
//! ```diff
//! NetDeviceNew(virtio::NetError),
diff --git a/src/parse.rs b/src/parse.rs
index 77fd4bc..98b8c86 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -1,6 +1,6 @@
use proc_macro2::Span;
use syn::parse::{Parse, ParseStream};
-use syn::{Attribute, Error, Expr, Result, Stmt, Token, Visibility};
+use syn::{Attribute, Error, Expr, Fields, Result, Stmt, Token, Visibility};
use crate::emit::Kind;
@@ -15,6 +15,7 @@ impl Parse for Nothing {
pub enum Input {
Enum(syn::ItemEnum),
Match(syn::ExprMatch),
+ Struct(syn::ItemStruct),
Let(syn::ExprMatch),
}
@@ -23,6 +24,7 @@ impl Input {
match self {
Input::Enum(_) => Kind::Enum,
Input::Match(_) => Kind::Match,
+ Input::Struct(_) => Kind::Struct,
Input::Let(_) => Kind::Let,
}
}
@@ -61,6 +63,14 @@ impl Parse for Input {
if ahead.peek(Token![enum]) {
return input.parse().map(Input::Enum);
}
+ if ahead.peek(Token![struct]) {
+ let input = input.parse().map(Input::Struct)?;
+ if let Input::Struct(ref item) = input {
+ if let Fields::Named(_) = item.fields {
+ return Ok(input);
+ }
+ }
+ }
Err(unexpected())
}
@@ -68,6 +78,6 @@ impl Parse for Input {
fn unexpected() -> Error {
let span = Span::call_site();
- let msg = "expected enum or match expression";
+ let msg = "expected enum, named struct, or match expression";
Error::new(span, msg)
}
diff --git a/tests/stable.rs b/tests/stable.rs
index 1bf49f4..a3b6a7b 100644
--- a/tests/stable.rs
+++ b/tests/stable.rs
@@ -1,3 +1,5 @@
+#![allow(dead_code)]
+
#[remain::sorted]
pub enum TestEnum {
A,
@@ -6,6 +8,14 @@ pub enum TestEnum {
D,
}
+#[remain::sorted]
+pub struct TestStruct {
+ a: usize,
+ b: usize,
+ c: usize,
+ d: usize,
+}
+
#[test]
#[remain::check]
fn test_match() {
diff --git a/tests/ui/struct.rs b/tests/ui/struct.rs
new file mode 100644
index 0000000..5f2cd41
--- /dev/null
+++ b/tests/ui/struct.rs
@@ -0,0 +1,11 @@
+use remain::sorted;
+
+#[sorted]
+struct TestStruct {
+ d: usize,
+ c: usize,
+ a: usize,
+ b: usize,
+}
+
+fn main() {}
diff --git a/tests/ui/struct.stderr b/tests/ui/struct.stderr
new file mode 100644
index 0000000..2ead15b
--- /dev/null
+++ b/tests/ui/struct.stderr
@@ -0,0 +1,5 @@
+error: c should sort before d
+ --> $DIR/struct.rs:6:5
+ |
+6 | c: usize,
+ | ^
diff --git a/tests/ui/unsupported.rs b/tests/ui/unsupported.rs
index b11c974..dbda20c 100644
--- a/tests/ui/unsupported.rs
+++ b/tests/ui/unsupported.rs
@@ -8,3 +8,9 @@ fn main() {
_ => {}
}
}
+
+#[remain::sorted]
+struct TestUnnamedStruct(usize, usize, usize, usize);
+
+#[remain::sorted]
+struct TestUnitStruct;
diff --git a/tests/ui/unsupported.stderr b/tests/ui/unsupported.stderr
index e471e1e..2e0d70f 100644
--- a/tests/ui/unsupported.stderr
+++ b/tests/ui/unsupported.stderr
@@ -3,3 +3,15 @@ error: unsupported by #[remain::sorted]
|
7 | 0..=20 => {}
| ^^^^^^
+
+error: expected enum, named struct, or match expression
+ --> $DIR/unsupported.rs:12:1
+ |
+12 | #[remain::sorted]
+ | ^^^^^^^^^^^^^^^^^
+
+error: expected enum, named struct, or match expression
+ --> $DIR/unsupported.rs:15:1
+ |
+15 | #[remain::sorted]
+ | ^^^^^^^^^^^^^^^^^
diff --git a/tests/unstable.rs b/tests/unstable.rs
index 827c59b..78dbd08 100644
--- a/tests/unstable.rs
+++ b/tests/unstable.rs
@@ -1,3 +1,4 @@
+#![allow(dead_code)]
#![cfg(not(remain_stable_testing))]
#![feature(proc_macro_hygiene, stmt_expr_attributes)]
@@ -9,6 +10,14 @@ pub enum TestEnum {
D,
}
+#[remain::sorted]
+pub struct TestStruct {
+ a: usize,
+ b: usize,
+ c: usize,
+ d: usize,
+}
+
#[test]
fn test_match() {
let value = TestEnum::A;