diff options
author | Chih-Hung Hsieh <chh@google.com> | 2020-02-27 10:42:41 -0800 |
---|---|---|
committer | Chih-Hung Hsieh <chh@google.com> | 2020-03-05 14:32:21 -0800 |
commit | 3095d1536defaeecd3bda0e10483589a2ebc1428 (patch) | |
tree | b234233124fbd257ac9a953c924c18ac97ffd996 /src | |
parent | 8ed7a370bef4647c82fe5b22b0696ddc715b706b (diff) | |
download | remain-3095d1536defaeecd3bda0e10483589a2ebc1428.tar.gz |
Add new remain-0.2.1android-r-preview-4android-r-preview-3android-r-preview-2
* Old remain-0.1.3 uses libsyn-0.15.42
* New remain-0.2.1 uses libsyn-1.0.7
* Fill back missing NOTICE,LICENSE,METADATA,MODULE_LICENSE_* files
Bug: 143477201
Bug: 150877376
Test: make
Change-Id: Ie33535733e26210ca2ca19ec3fd00f5649faa4db
Diffstat (limited to 'src')
-rw-r--r-- | src/check.rs | 83 | ||||
-rw-r--r-- | src/compare.rs | 45 | ||||
-rw-r--r-- | src/emit.rs | 39 | ||||
-rw-r--r-- | src/format.rs | 27 | ||||
-rw-r--r-- | src/lib.rs | 176 | ||||
-rw-r--r-- | src/parse.rs | 80 | ||||
-rw-r--r-- | src/visit.rs | 77 |
7 files changed, 0 insertions, 527 deletions
diff --git a/src/check.rs b/src/check.rs deleted file mode 100644 index 0995078..0000000 --- a/src/check.rs +++ /dev/null @@ -1,83 +0,0 @@ -use syn::{Arm, Ident, Result, Variant}; -use syn::{Error, Field, Pat, PatIdent}; - -use crate::compare::Path; -use crate::format; -use crate::parse::Input::{self, *}; - -pub fn sorted(input: Input) -> Result<()> { - let paths = match input { - Enum(item) => collect_paths(item.variants)?, - Struct(fields) => collect_paths(fields.named)?, - Match(expr) | Let(expr) => collect_paths(expr.arms)?, - }; - - for i in 1..paths.len() { - let cur = &paths[i]; - if *cur < paths[i - 1] { - let lesser = cur; - let correct_pos = paths[..i - 1].binary_search(cur).unwrap_err(); - let greater = &paths[correct_pos]; - return Err(format::error(lesser, greater)); - } - } - - Ok(()) -} - -fn collect_paths<I>(iter: I) -> Result<Vec<Path>> -where - I: IntoIterator, - I::Item: IntoPath, -{ - iter.into_iter().map(IntoPath::into_path).collect() -} - -trait IntoPath { - fn into_path(self) -> Result<Path>; -} - -impl IntoPath for Variant { - fn into_path(self) -> Result<Path> { - Ok(Path { - segments: vec![self.ident], - }) - } -} - -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. - let pat = self.pats.into_iter().next().expect("at least one pat"); - - let segments = match pat { - Pat::Wild(pat) => vec![Ident::from(pat.underscore_token)], - Pat::Path(pat) => idents_of_path(pat.path), - Pat::Struct(pat) => idents_of_path(pat.path), - Pat::TupleStruct(pat) => idents_of_path(pat.path), - Pat::Ident(ref pat) if is_just_ident(pat) => vec![pat.ident.clone()], - other => { - let msg = "unsupported by #[remain::sorted]"; - return Err(Error::new_spanned(other, msg)); - } - }; - - Ok(Path { segments }) - } -} - -fn idents_of_path(path: syn::Path) -> Vec<Ident> { - path.segments.into_iter().map(|seg| seg.ident).collect() -} - -fn is_just_ident(pat: &PatIdent) -> bool { - pat.by_ref.is_none() && pat.mutability.is_none() && pat.subpat.is_none() -} diff --git a/src/compare.rs b/src/compare.rs deleted file mode 100644 index 59f997d..0000000 --- a/src/compare.rs +++ /dev/null @@ -1,45 +0,0 @@ -use proc_macro2::Ident; -use std::cmp::Ordering; - -#[derive(PartialEq, Eq)] -pub struct Path { - pub segments: Vec<Ident>, -} - -impl PartialOrd for Path { - fn partial_cmp(&self, other: &Path) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for Path { - fn cmp(&self, other: &Path) -> Ordering { - // Lexicographic ordering across path segments. - for (lhs, rhs) in self.segments.iter().zip(&other.segments) { - match cmp(&lhs.to_string(), &rhs.to_string()) { - Ordering::Equal => {} - non_eq => return non_eq, - } - } - - self.segments.len().cmp(&other.segments.len()) - } -} - -// TODO: more intelligent comparison -// for example to handle numeric cases like E9 < E10. -fn cmp(lhs: &str, rhs: &str) -> Ordering { - // Sort `_` last. - match (lhs == "_", rhs == "_") { - (true, true) => return Ordering::Equal, - (true, false) => return Ordering::Greater, - (false, true) => return Ordering::Less, - (false, false) => {} - } - - let lhs = lhs.to_ascii_lowercase(); - let rhs = rhs.to_ascii_lowercase(); - - // For now: asciibetical ordering. - lhs.cmp(&rhs) -} diff --git a/src/emit.rs b/src/emit.rs deleted file mode 100644 index d1ddda8..0000000 --- a/src/emit.rs +++ /dev/null @@ -1,39 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::quote; -use syn::Error; - -#[derive(Copy, Clone)] -pub enum Kind { - Enum, - Match, - Struct, - Let, -} - -pub fn emit(err: Error, kind: Kind, original: TokenStream) -> TokenStream { - let mut err = err; - if !probably_has_spans(kind) { - // Otherwise the error is printed without any line number. - err = Error::new(Span::call_site(), &err.to_string()); - } - - let err = err.to_compile_error(); - let original = proc_macro2::TokenStream::from(original); - - let expanded = match kind { - Kind::Enum | Kind::Let | Kind::Struct => quote!(#err #original), - Kind::Match => quote!({ #err #original }), - }; - - TokenStream::from(expanded) -} - -// Rustc is so bad at spans. -// https://github.com/rust-lang/rust/issues/43081 -fn probably_has_spans(kind: Kind) -> bool { - match kind { - Kind::Enum | Kind::Struct => true, - Kind::Match | Kind::Let => false, - } -} diff --git a/src/format.rs b/src/format.rs deleted file mode 100644 index 5832643..0000000 --- a/src/format.rs +++ /dev/null @@ -1,27 +0,0 @@ -use proc_macro2::TokenStream; -use quote::TokenStreamExt; -use std::fmt::{self, Display}; -use syn::Error; - -use crate::compare::Path; - -impl Display for Path { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - for (i, segment) in self.segments.iter().enumerate() { - if i > 0 { - formatter.write_str("::")?; - } - segment.fmt(formatter)?; - } - Ok(()) - } -} - -pub fn error(lesser: &Path, greater: &Path) -> Error { - let mut spans = TokenStream::new(); - spans.append_all(&lesser.segments); - - let msg = format!("{} should sort before {}", lesser, greater); - - Error::new_spanned(spans, msg) -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 967143e..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! This crate provides an attribute macro to check at compile time that the -//! variants of an enum or the arms of a match expression are written in sorted -//! order. -//! -//! # Syntax -//! -//! Place a `#[remain::sorted]` attribute on enums, structs, match-expressions, -//! or let-statements whose value is a match-expression. -//! -//! Alternatively, import as `use remain::sorted;` and use `#[sorted]` as the -//! attribute. -//! -//! ``` -//! # use std::error::Error as StdError; -//! # use std::fmt::{self, Display}; -//! # use std::io; -//! # -//! #[remain::sorted] -//! #[derive(Debug)] -//! pub enum Error { -//! BlockSignal(signal::Error), -//! CreateCrasClient(libcras::Error), -//! CreateEventFd(sys_util::Error), -//! CreateSignalFd(sys_util::SignalFdError), -//! CreateSocket(io::Error), -//! DetectImageType(qcow::Error), -//! DeviceJail(io_jail::Error), -//! NetDeviceNew(virtio::NetError), -//! SpawnVcpu(io::Error), -//! } -//! -//! impl Display for Error { -//! # #[remain::check] -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! use self::Error::*; -//! -//! #[remain::sorted] -//! match self { -//! BlockSignal(e) => write!(f, "failed to block signal: {}", e), -//! CreateCrasClient(e) => write!(f, "failed to create cras client: {}", e), -//! CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e), -//! CreateSignalFd(e) => write!(f, "failed to create signalfd: {}", e), -//! CreateSocket(e) => write!(f, "failed to create socket: {}", e), -//! DetectImageType(e) => write!(f, "failed to detect disk image type: {}", e), -//! DeviceJail(e) => write!(f, "failed to jail device: {}", e), -//! NetDeviceNew(e) => write!(f, "failed to set up virtio networking: {}", e), -//! SpawnVcpu(e) => write!(f, "failed to spawn VCPU thread: {}", e), -//! } -//! } -//! } -//! # -//! # mod signal { -//! # pub use std::io::Error; -//! # } -//! # -//! # mod libcras { -//! # pub use std::io::Error; -//! # } -//! # -//! # mod sys_util { -//! # pub use std::io::{Error, Error as SignalFdError}; -//! # } -//! # -//! # mod qcow { -//! # pub use std::io::Error; -//! # } -//! # -//! # mod io_jail { -//! # pub use std::io::Error; -//! # } -//! # -//! # mod virtio { -//! # pub use std::io::Error as NetError; -//! # } -//! # -//! # fn main() {} -//! ``` -//! -//! If an enum variant, struct field, or match arm is inserted out of order,\ -//! -//! ```diff -//! NetDeviceNew(virtio::NetError), -//! SpawnVcpu(io::Error), -//! + AaaUhOh(Box<dyn StdError>), -//! } -//! ``` -//! -//! then the macro produces a compile error. -//! -//! ```console -//! error: AaaUhOh should sort before BlockSignal -//! --> tests/stable.rs:49:5 -//! | -//! 49 | AaaUhOh(Box<dyn StdError>), -//! | ^^^^^^^ -//! ``` -//! -//! # Compiler support -//! -//! The attribute on enums 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 a nightly compiler and the following two features enabled: -//! -//! ``` -//! # const IGNORE: &str = stringify! { -//! #![feature(proc_macro_hygiene, stmt_expr_attributes)] -//! # }; -//! ``` -//! -//! As a stable alternative, this crate provides a function-level attribute -//! called `#[remain::check]` which makes match-expression and let-statement -//! attributes work on any rustc version 1.31+. Place this attribute on any -//! function containing `#[sorted]` to make them work on a stable compiler. -//! -//! ``` -//! # use std::fmt::{self, Display}; -//! # -//! # enum Error {} -//! # -//! impl Display for Error { -//! #[remain::check] -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! use self::Error::*; -//! -//! #[sorted] -//! match self { -//! /* ... */ -//! # _ => unimplemented!(), -//! } -//! } -//! } -//! # -//! # fn main() {} -//! ``` - -extern crate proc_macro; - -mod check; -mod compare; -mod emit; -mod format; -mod parse; -mod visit; - -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, ItemFn}; - -use crate::emit::emit; -use crate::parse::{Input, Nothing}; - -#[proc_macro_attribute] -pub fn sorted(args: TokenStream, input: TokenStream) -> TokenStream { - let original = input.clone(); - - let _ = parse_macro_input!(args as Nothing); - let input = parse_macro_input!(input as Input); - let kind = input.kind(); - - match check::sorted(input) { - Ok(()) => original, - Err(err) => emit(err, kind, original), - } -} - -#[proc_macro_attribute] -pub fn check(args: TokenStream, input: TokenStream) -> TokenStream { - let _ = parse_macro_input!(args as Nothing); - let mut input = parse_macro_input!(input as ItemFn); - - visit::check(&mut input); - - TokenStream::from(quote!(#input)) -} diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index 97bd9f8..0000000 --- a/src/parse.rs +++ /dev/null @@ -1,80 +0,0 @@ -use proc_macro2::Span; -use syn::parse::{Parse, ParseStream}; -use syn::{Attribute, Error, Expr, Fields, Result, Stmt, Token, Visibility}; - -use crate::emit::Kind; - -pub struct Nothing; - -impl Parse for Nothing { - fn parse(_input: ParseStream) -> Result<Self> { - Ok(Nothing) - } -} - -pub enum Input { - Enum(syn::ItemEnum), - Match(syn::ExprMatch), - Struct(syn::FieldsNamed), - Let(syn::ExprMatch), -} - -impl Input { - pub fn kind(&self) -> Kind { - match self { - Input::Enum(_) => Kind::Enum, - Input::Match(_) => Kind::Match, - Input::Struct(_) => Kind::Struct, - Input::Let(_) => Kind::Let, - } - } -} - -impl Parse for Input { - fn parse(input: ParseStream) -> Result<Self> { - let _ = input.call(Attribute::parse_outer)?; - - if input.peek(Token![match]) { - let expr = match input.parse()? { - Expr::Match(expr) => expr, - _ => unreachable!("expected match"), - }; - return Ok(Input::Match(expr)); - } - - if input.peek(Token![let]) { - let stmt = match input.parse()? { - Stmt::Local(stmt) => stmt, - _ => unreachable!("expected let"), - }; - let init = match stmt.init { - Some((_, init)) => *init, - None => return Err(unexpected()), - }; - let expr = match init { - Expr::Match(expr) => expr, - _ => return Err(unexpected()), - }; - return Ok(Input::Let(expr)); - } - - let ahead = input.fork(); - let _: Visibility = ahead.parse()?; - if ahead.peek(Token![enum]) { - return input.parse().map(Input::Enum); - } else if ahead.peek(Token![struct]) { - let input: syn::ItemStruct = input.parse()?; - if let Fields::Named(fields) = input.fields { - return Ok(Input::Struct(fields)); - } - } - - Err(unexpected()) - } -} - -fn unexpected() -> Error { - let span = Span::call_site(); - let msg = "expected enum, struct, or match expression"; - Error::new(span, msg) -} diff --git a/src/visit.rs b/src/visit.rs deleted file mode 100644 index f681aa9..0000000 --- a/src/visit.rs +++ /dev/null @@ -1,77 +0,0 @@ -use quote::quote; -use syn::visit_mut::{self, VisitMut}; -use syn::{parse_quote, Attribute, Expr, ExprMatch, ItemFn, Local}; - -use crate::parse::Input; - -pub fn check(input: &mut ItemFn) { - Checker.visit_item_fn_mut(input); -} - -struct Checker; - -impl VisitMut for Checker { - fn visit_expr_mut(&mut self, expr: &mut Expr) { - visit_mut::visit_expr_mut(self, expr); - - let expr_match = match expr { - Expr::Match(expr) => expr, - _ => return, - }; - - if !take_sorted_attr(&mut expr_match.attrs) { - return; - } - - let input = expr_match.clone(); - check_and_insert_error(input, expr); - } - - fn visit_local_mut(&mut self, local: &mut Local) { - visit_mut::visit_local_mut(self, local); - - let init = match &local.init { - Some((_, init)) => init, - None => return, - }; - - let expr_match = match init.as_ref() { - Expr::Match(expr) => expr, - _ => return, - }; - - if !take_sorted_attr(&mut local.attrs) { - return; - } - - let input = expr_match.clone(); - let expr = local.init.as_mut().unwrap().1.as_mut(); - check_and_insert_error(input, expr); - } -} - -fn take_sorted_attr(attrs: &mut Vec<Attribute>) -> bool { - for i in 0..attrs.len() { - let path = &attrs[i].path; - let path = quote!(#path).to_string(); - if path == "sorted" || path == "remain :: sorted" { - attrs.remove(i); - return true; - } - } - - false -} - -fn check_and_insert_error(input: ExprMatch, out: &mut Expr) { - let original = quote!(#input); - let input = Input::Match(input); - - if let Err(err) = crate::check::sorted(input) { - let err = err.to_compile_error(); - *out = parse_quote!({ - #err - #original - }); - } -} |