diff options
author | Matthew Maurer <mmaurer@google.com> | 2023-05-26 20:23:04 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-05-26 20:23:04 +0000 |
commit | a759152d73848994979f9d1f3ea9fc1aa8134827 (patch) | |
tree | 9fc06f5a5d569b3a692bae365da57117c240fff2 | |
parent | 017cab358ebcff2da4da1e9034212024ee5380a1 (diff) | |
parent | ee6cb37112f18c4a56548c628e9c44925980c728 (diff) | |
download | prettyplease-a759152d73848994979f9d1f3ea9fc1aa8134827.tar.gz |
Upgrade prettyplease to 0.2.6 am: ee6cb37112
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/prettyplease/+/2601405
Change-Id: Iff1fff0d6014016ce2ffb7883335a16cfaaf5775
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 12 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | Cargo.toml | 11 | ||||
-rw-r--r-- | Cargo.toml.orig | 34 | ||||
-rw-r--r-- | METADATA | 13 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/algorithm.rs | 2 | ||||
-rw-r--r-- | src/attr.rs | 56 | ||||
-rw-r--r-- | src/data.rs | 31 | ||||
-rw-r--r-- | src/expr.rs | 424 | ||||
-rw-r--r-- | src/generics.rs | 103 | ||||
-rw-r--r-- | src/item.rs | 986 | ||||
-rw-r--r-- | src/lib.rs | 6 | ||||
-rw-r--r-- | src/lit.rs | 2 | ||||
-rw-r--r-- | src/mac.rs | 52 | ||||
-rw-r--r-- | src/pat.rs | 118 | ||||
-rw-r--r-- | src/path.rs | 89 | ||||
-rw-r--r-- | src/stmt.rs | 146 | ||||
-rw-r--r-- | src/ty.rs | 92 |
20 files changed, 1655 insertions, 530 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 3e35b72..2ede8cc 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "81780e0ebdb1f8df7c36b39a02e606e60581dce9" + "sha1": "f69383aff4049036f11f3a3b4b927f28f96fa60f" }, "path_in_vcs": "" }
\ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca27e6a..fa508cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,18 @@ jobs: - run: cargo run --manifest-path cargo-expand/update/Cargo.toml - run: git diff --exit-code + fuzz: + name: Fuzz + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/install@cargo-fuzz + - run: cargo fuzz check + clippy: name: Clippy runs-on: ubuntu-latest @@ -7,7 +7,7 @@ rust_library_host { name: "libprettyplease", crate_name: "prettyplease", cargo_env_compat: true, - cargo_pkg_version: "0.1.25", + cargo_pkg_version: "0.2.6", srcs: ["src/lib.rs"], edition: "2021", rustlibs: [ @@ -13,9 +13,9 @@ edition = "2021" rust-version = "1.56" name = "prettyplease" -version = "0.1.25" +version = "0.2.6" authors = ["David Tolnay <dtolnay@gmail.com>"] -links = "prettyplease01" +links = "prettyplease02" exclude = ["cargo-expand"] autoexamples = false description = "A minimal `syn` syntax tree pretty-printer" @@ -26,6 +26,9 @@ categories = ["development-tools"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/prettyplease" +[package.metadata.playground] +features = ["verbatim"] + [lib] doc-scrape-examples = false @@ -34,12 +37,12 @@ version = "1.0" default-features = false [dependencies.syn] -version = "1.0.90" +version = "2.0.16" features = ["full"] default-features = false [dev-dependencies.syn] -version = "1.0.90" +version = "2.0.16" features = ["parsing"] default-features = false diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..c902b61 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,34 @@ +[package] +name = "prettyplease" +version = "0.2.6" +authors = ["David Tolnay <dtolnay@gmail.com>"] +autoexamples = false +categories = ["development-tools"] +description = "A minimal `syn` syntax tree pretty-printer" +documentation = "https://docs.rs/prettyplease" +edition = "2021" +exclude = ["cargo-expand"] +keywords = ["rustfmt"] +license = "MIT OR Apache-2.0" +links = "prettyplease02" +repository = "https://github.com/dtolnay/prettyplease" +rust-version = "1.56" + +[features] +verbatim = ["syn/parsing"] + +[dependencies] +proc-macro2 = { version = "1.0", default-features = false } +syn = { version = "2.0.16", default-features = false, features = ["full"] } + +[dev-dependencies] +syn = { version = "2.0.16", default-features = false, features = ["parsing"] } + +[lib] +doc-scrape-examples = false + +[package.metadata.playground] +features = ["verbatim"] + +[workspace] +members = ["cargo-expand/update", "examples/update"] @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/prettyplease +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "prettyplease" description: "A minimal `syn` syntax tree pretty-printer" third_party { @@ -7,14 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/prettyplease/prettyplease-0.1.25.crate" + value: "https://static.crates.io/crates/prettyplease/prettyplease-0.2.6.crate" } - version: "0.1.25" - # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same. + version: "0.2.6" license_type: NOTICE last_upgrade_date { year: 2023 - month: 3 - day: 22 + month: 5 + day: 23 } } @@ -167,8 +167,8 @@ from rustfmt-formatted code. ```rust // [dependencies] -// prettyplease = "0.1" -// syn = { version = "1", default-features = false, features = ["full", "parsing"] } +// prettyplease = "0.2" +// syn = { version = "2", default-features = false, features = ["full", "parsing"] } const INPUT: &str = stringify! { use crate::{ diff --git a/src/algorithm.rs b/src/algorithm.rs index b6dea57..6e2b961 100644 --- a/src/algorithm.rs +++ b/src/algorithm.rs @@ -251,7 +251,7 @@ impl Printer { fn check_stack(&mut self, mut depth: usize) { while let Some(&index) = self.scan_stack.back() { - let mut entry = &mut self.buf[index]; + let entry = &mut self.buf[index]; match entry.token { Token::Begin(_) => { if depth == 0 { diff --git a/src/attr.rs b/src/attr.rs index cd36ce5..0388d66 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,7 +1,8 @@ use crate::algorithm::Printer; +use crate::path::PathKind; use crate::INDENT; -use proc_macro2::{Delimiter, TokenStream, TokenTree}; -use syn::{AttrStyle, Attribute, Lit, PathArguments}; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use syn::{AttrStyle, Attribute, Expr, Lit, MacroDelimiter, Meta, MetaList, MetaNameValue}; impl Printer { pub fn outer_attrs(&mut self, attrs: &[Attribute]) { @@ -74,12 +75,36 @@ impl Printer { AttrStyle::Inner(_) => "#!", }); self.word("["); - self.path(&attr.path); - self.attr_tokens(attr.tokens.clone()); + self.meta(&attr.meta); self.word("]"); self.space(); } + fn meta(&mut self, meta: &Meta) { + match meta { + Meta::Path(path) => self.path(path, PathKind::Simple), + Meta::List(meta) => self.meta_list(meta), + Meta::NameValue(meta) => self.meta_name_value(meta), + } + } + + fn meta_list(&mut self, meta: &MetaList) { + self.path(&meta.path, PathKind::Simple); + let delimiter = match meta.delimiter { + MacroDelimiter::Paren(_) => Delimiter::Parenthesis, + MacroDelimiter::Brace(_) => Delimiter::Brace, + MacroDelimiter::Bracket(_) => Delimiter::Bracket, + }; + let group = Group::new(delimiter, meta.tokens.clone()); + self.attr_tokens(TokenStream::from(TokenTree::Group(group))); + } + + fn meta_name_value(&mut self, meta: &MetaNameValue) { + self.path(&meta.path, PathKind::Simple); + self.word(" = "); + self.expr(&meta.value); + } + fn attr_tokens(&mut self, tokens: TokenStream) { let mut stack = Vec::new(); stack.push((tokens.into_iter().peekable(), Delimiter::None)); @@ -184,26 +209,15 @@ impl Printer { } fn value_of_attribute(requested: &str, attr: &Attribute) -> Option<String> { - let is_doc = attr.path.leading_colon.is_none() - && attr.path.segments.len() == 1 - && matches!(attr.path.segments[0].arguments, PathArguments::None) - && attr.path.segments[0].ident == requested; - if !is_doc { - return None; - } - let mut tokens = attr.tokens.clone().into_iter(); - match tokens.next() { - Some(TokenTree::Punct(punct)) if punct.as_char() == '=' => {} + let value = match &attr.meta { + Meta::NameValue(meta) if meta.path.is_ident(requested) => &meta.value, _ => return None, - } - let literal = match tokens.next() { - Some(TokenTree::Literal(literal)) => literal, + }; + let lit = match value { + Expr::Lit(expr) if expr.attrs.is_empty() => &expr.lit, _ => return None, }; - if tokens.next().is_some() { - return None; - } - match Lit::new(literal) { + match lit { Lit::Str(string) => Some(string.value()), _ => None, } diff --git a/src/data.rs b/src/data.rs index 7767981..d823ee3 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,10 +1,8 @@ use crate::algorithm::Printer; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::INDENT; -use syn::{ - Field, Fields, FieldsUnnamed, PathArguments, Variant, VisCrate, VisPublic, VisRestricted, - Visibility, -}; +use syn::{Field, Fields, FieldsUnnamed, Variant, VisRestricted, Visibility}; impl Printer { pub fn variant(&mut self, variant: &Variant) { @@ -60,36 +58,21 @@ impl Printer { pub fn visibility(&mut self, vis: &Visibility) { match vis { - Visibility::Public(vis) => self.vis_public(vis), - Visibility::Crate(vis) => self.vis_crate(vis), + Visibility::Public(_) => self.word("pub "), Visibility::Restricted(vis) => self.vis_restricted(vis), Visibility::Inherited => {} } } - fn vis_public(&mut self, vis: &VisPublic) { - let _ = vis; - self.word("pub "); - } - - fn vis_crate(&mut self, vis: &VisCrate) { - let _ = vis; - self.word("crate "); - } - fn vis_restricted(&mut self, vis: &VisRestricted) { self.word("pub("); - let omit_in = vis.path.leading_colon.is_none() - && vis.path.segments.len() == 1 - && matches!(vis.path.segments[0].arguments, PathArguments::None) - && matches!( - vis.path.segments[0].ident.to_string().as_str(), - "self" | "super" | "crate", - ); + let omit_in = vis.path.get_ident().map_or(false, |ident| { + matches!(ident.to_string().as_str(), "self" | "super" | "crate") + }); if !omit_in { self.word("in "); } - self.path(&vis.path); + self.path(&vis.path, PathKind::Simple); self.word(") "); } } diff --git a/src/expr.rs b/src/expr.rs index 0689595..92c50e7 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,18 +1,19 @@ use crate::algorithm::{BreakToken, Printer}; use crate::attr; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::stmt; use crate::INDENT; use proc_macro2::TokenStream; use syn::punctuated::Punctuated; use syn::{ - token, Arm, Attribute, BinOp, Block, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, - ExprAwait, ExprBinary, ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, - ExprContinue, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, + token, Arm, Attribute, BinOp, Block, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, + ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprConst, ExprContinue, + ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, - ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, - ExprUnsafe, ExprWhile, ExprYield, FieldValue, GenericMethodArgument, Index, Label, Member, - MethodTurbofish, PathArguments, QSelf, RangeLimits, ReturnType, Stmt, Token, UnOp, + ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, + ExprWhile, ExprYield, FieldValue, Index, Label, Member, RangeLimits, ReturnType, Stmt, Token, + UnOp, }; impl Printer { @@ -20,22 +21,22 @@ impl Printer { match expr { Expr::Array(expr) => self.expr_array(expr), Expr::Assign(expr) => self.expr_assign(expr), - Expr::AssignOp(expr) => self.expr_assign_op(expr), Expr::Async(expr) => self.expr_async(expr), Expr::Await(expr) => self.expr_await(expr, false), Expr::Binary(expr) => self.expr_binary(expr), Expr::Block(expr) => self.expr_block(expr), - Expr::Box(expr) => self.expr_box(expr), Expr::Break(expr) => self.expr_break(expr), Expr::Call(expr) => self.expr_call(expr, false), Expr::Cast(expr) => self.expr_cast(expr), Expr::Closure(expr) => self.expr_closure(expr), + Expr::Const(expr) => self.expr_const(expr), Expr::Continue(expr) => self.expr_continue(expr), Expr::Field(expr) => self.expr_field(expr, false), Expr::ForLoop(expr) => self.expr_for_loop(expr), Expr::Group(expr) => self.expr_group(expr), Expr::If(expr) => self.expr_if(expr), Expr::Index(expr) => self.expr_index(expr, false), + Expr::Infer(expr) => self.expr_infer(expr), Expr::Let(expr) => self.expr_let(expr), Expr::Lit(expr) => self.expr_lit(expr), Expr::Loop(expr) => self.expr_loop(expr), @@ -52,7 +53,6 @@ impl Printer { Expr::Try(expr) => self.expr_try(expr, false), Expr::TryBlock(expr) => self.expr_try_block(expr), Expr::Tuple(expr) => self.expr_tuple(expr), - Expr::Type(expr) => self.expr_type(expr), Expr::Unary(expr) => self.expr_unary(expr), Expr::Unsafe(expr) => self.expr_unsafe(expr), Expr::Verbatim(expr) => self.expr_verbatim(expr), @@ -90,8 +90,6 @@ impl Printer { } } - // If the given expression is a bare `ExprStruct`, wraps it in parenthesis - // before appending it to `TokenStream`. fn wrap_exterior_struct(&mut self, expr: &Expr) { let needs_paren = contains_exterior_struct_lit(expr); if needs_paren { @@ -133,19 +131,6 @@ impl Printer { self.end(); } - fn expr_assign_op(&mut self, expr: &ExprAssignOp) { - self.outer_attrs(&expr.attrs); - self.ibox(INDENT); - self.ibox(-INDENT); - self.expr(&expr.left); - self.end(); - self.space(); - self.binary_operator(&expr.op); - self.nbsp(); - self.expr(&expr.right); - self.end(); - } - fn expr_async(&mut self, expr: &ExprAsync) { self.outer_attrs(&expr.attrs); self.word("async "); @@ -193,12 +178,6 @@ impl Printer { self.end(); } - fn expr_box(&mut self, expr: &ExprBox) { - self.outer_attrs(&expr.attrs); - self.word("box "); - self.expr(&expr.expr); - } - fn expr_break(&mut self, expr: &ExprBreak) { self.outer_attrs(&expr.attrs); self.word("break"); @@ -242,12 +221,18 @@ impl Printer { fn expr_closure(&mut self, expr: &ExprClosure) { self.outer_attrs(&expr.attrs); self.ibox(0); - if expr.asyncness.is_some() { - self.word("async "); + if let Some(bound_lifetimes) = &expr.lifetimes { + self.bound_lifetimes(bound_lifetimes); + } + if expr.constness.is_some() { + self.word("const "); } if expr.movability.is_some() { self.word("static "); } + if expr.asyncness.is_some() { + self.word("async "); + } if expr.capture.is_some() { self.word("move "); } @@ -311,6 +296,14 @@ impl Printer { self.end(); } + pub fn expr_const(&mut self, expr: &ExprConst) { + self.outer_attrs(&expr.attrs); + self.word("const "); + self.cbox(INDENT); + self.small_block(&expr.block, &expr.attrs); + self.end(); + } + fn expr_continue(&mut self, expr: &ExprContinue) { self.outer_attrs(&expr.attrs); self.word("continue"); @@ -435,6 +428,11 @@ impl Printer { self.word("]"); } + fn expr_infer(&mut self, expr: &ExprInfer) { + self.outer_attrs(&expr.attrs); + self.word("_"); + } + fn expr_let(&mut self, expr: &ExprLet) { self.outer_attrs(&expr.attrs); self.ibox(INDENT); @@ -477,9 +475,10 @@ impl Printer { self.word("}"); } - fn expr_macro(&mut self, expr: &ExprMacro) { + pub fn expr_macro(&mut self, expr: &ExprMacro) { self.outer_attrs(&expr.attrs); - self.mac(&expr.mac, None); + let semicolon = false; + self.mac(&expr.mac, None, semicolon); } fn expr_match(&mut self, expr: &ExprMatch) { @@ -521,7 +520,7 @@ impl Printer { self.word("."); self.ident(&expr.method); if let Some(turbofish) = &expr.turbofish { - self.method_turbofish(turbofish); + self.angle_bracketed_generic_arguments(turbofish, PathKind::Expr); } self.cbox(if unindent_call_args { -INDENT } else { 0 }); self.word("("); @@ -537,22 +536,22 @@ impl Printer { self.word(")"); } - fn expr_path(&mut self, expr: &ExprPath) { + pub fn expr_path(&mut self, expr: &ExprPath) { self.outer_attrs(&expr.attrs); - self.qpath(&expr.qself, &expr.path); + self.qpath(&expr.qself, &expr.path, PathKind::Expr); } - fn expr_range(&mut self, expr: &ExprRange) { + pub fn expr_range(&mut self, expr: &ExprRange) { self.outer_attrs(&expr.attrs); - if let Some(from) = &expr.from { - self.expr(from); + if let Some(start) = &expr.start { + self.expr(start); } self.word(match expr.limits { RangeLimits::HalfOpen(_) => "..", RangeLimits::Closed(_) => "..=", }); - if let Some(to) = &expr.to { - self.expr(to); + if let Some(end) = &expr.end { + self.expr(end); } } @@ -584,14 +583,10 @@ impl Printer { } fn expr_struct(&mut self, expr: &ExprStruct) { - self.expr_qualified_struct(&None, expr); - } - - fn expr_qualified_struct(&mut self, qself: &Option<QSelf>, expr: &ExprStruct) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); self.ibox(-INDENT); - self.qpath(qself, &expr.path); + self.qpath(&expr.qself, &expr.path, PathKind::Expr); self.end(); self.word(" {"); self.space_if_nonempty(); @@ -647,18 +642,6 @@ impl Printer { self.word(")"); } - fn expr_type(&mut self, expr: &ExprType) { - self.outer_attrs(&expr.attrs); - self.ibox(INDENT); - self.ibox(-INDENT); - self.expr(&expr.expr); - self.end(); - self.space(); - self.word(": "); - self.ty(&expr.ty); - self.end(); - } - fn expr_unary(&mut self, expr: &ExprUnary) { self.outer_attrs(&expr.attrs); self.unary_operator(&expr.op); @@ -667,23 +650,10 @@ impl Printer { fn expr_unsafe(&mut self, expr: &ExprUnsafe) { self.outer_attrs(&expr.attrs); - self.word("unsafe {"); + self.word("unsafe "); self.cbox(INDENT); - self.space_if_nonempty(); - self.inner_attrs(&expr.attrs); - for stmt in expr.block.stmts.iter().delimited() { - if stmt.is_first && stmt.is_last { - if let Stmt::Expr(expr) = &*stmt { - self.expr(expr); - self.space(); - continue; - } - } - self.stmt(&stmt); - } - self.offset(-INDENT); + self.small_block(&expr.block, &expr.attrs); self.end(); - self.word("}"); } #[cfg(not(feature = "verbatim"))] @@ -696,15 +666,17 @@ impl Printer { #[cfg(feature = "verbatim")] fn expr_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; - use syn::{braced, BoundLifetimes}; + use syn::{parenthesized, Ident}; enum ExprVerbatim { Empty, - Infer, + Builtin(Builtin), RawReference(RawReference), - ConstBlock(ConstBlock), - ClosureWithLifetimes(ClosureWithLifetimes), - QualifiedStruct(QualifiedStruct), + } + + struct Builtin { + name: Ident, + args: TokenStream, } struct RawReference { @@ -712,22 +684,8 @@ impl Printer { expr: Expr, } - struct ConstBlock { - attrs: Vec<Attribute>, - block: Block, - } - - struct ClosureWithLifetimes { - lifetimes: BoundLifetimes, - closure: ExprClosure, - } - - struct QualifiedStruct { - qself: QSelf, - strct: ExprStruct, - } - mod kw { + syn::custom_keyword!(builtin); syn::custom_keyword!(raw); } @@ -736,9 +694,14 @@ impl Printer { let lookahead = input.lookahead1(); if input.is_empty() { Ok(ExprVerbatim::Empty) - } else if lookahead.peek(Token![_]) { - input.parse::<Token![_]>()?; - Ok(ExprVerbatim::Infer) + } else if lookahead.peek(kw::builtin) { + input.parse::<kw::builtin>()?; + input.parse::<Token![#]>()?; + let name: Ident = input.parse()?; + let args; + parenthesized!(args in input); + let args: TokenStream = args.parse()?; + Ok(ExprVerbatim::Builtin(Builtin { name, args })) } else if lookahead.peek(Token![&]) { input.parse::<Token![&]>()?; input.parse::<kw::raw>()?; @@ -748,53 +711,6 @@ impl Printer { } let expr: Expr = input.parse()?; Ok(ExprVerbatim::RawReference(RawReference { mutable, expr })) - } else if lookahead.peek(Token![const]) { - input.parse::<Token![const]>()?; - let content; - let brace_token = braced!(content in input); - let attrs = content.call(Attribute::parse_inner)?; - let stmts = content.call(Block::parse_within)?; - Ok(ExprVerbatim::ConstBlock(ConstBlock { - attrs, - block: Block { brace_token, stmts }, - })) - } else if lookahead.peek(Token![for]) { - let lifetimes = input.parse()?; - let closure = input.parse()?; - Ok(ExprVerbatim::ClosureWithLifetimes(ClosureWithLifetimes { - lifetimes, - closure, - })) - } else if lookahead.peek(Token![<]) { - let path: ExprPath = input.parse()?; - let content; - let mut expr = QualifiedStruct { - qself: path.qself.unwrap(), - strct: ExprStruct { - attrs: Vec::new(), - brace_token: braced!(content in input), - path: path.path, - fields: Punctuated::new(), - dot2_token: None, - rest: None, - }, - }; - while !content.is_empty() { - if content.peek(Token![..]) { - expr.strct.dot2_token = Some(content.parse()?); - if !content.is_empty() { - expr.strct.rest = Some(Box::new(content.parse()?)); - } - break; - } - expr.strct.fields.push(content.parse()?); - if content.is_empty() { - break; - } - let punct: Token![,] = content.parse()?; - expr.strct.fields.push_punct(punct); - } - Ok(ExprVerbatim::QualifiedStruct(expr)) } else { Err(lookahead.error()) } @@ -808,28 +724,27 @@ impl Printer { match expr { ExprVerbatim::Empty => {} - ExprVerbatim::Infer => { - self.word("_"); + ExprVerbatim::Builtin(expr) => { + self.word("builtin # "); + self.ident(&expr.name); + self.word("("); + if !expr.args.is_empty() { + self.cbox(INDENT); + self.zerobreak(); + self.ibox(0); + self.macro_rules_tokens(expr.args, false); + self.end(); + self.zerobreak(); + self.offset(-INDENT); + self.end(); + } + self.word(")"); } ExprVerbatim::RawReference(expr) => { self.word("&raw "); self.word(if expr.mutable { "mut " } else { "const " }); self.expr(&expr.expr); } - ExprVerbatim::ConstBlock(expr) => { - self.outer_attrs(&expr.attrs); - self.cbox(INDENT); - self.word("const "); - self.small_block(&expr.block, &expr.attrs); - self.end(); - } - ExprVerbatim::ClosureWithLifetimes(expr) => { - self.bound_lifetimes(&expr.lifetimes); - self.expr_closure(&expr.closure); - } - ExprVerbatim::QualifiedStruct(expr) => { - self.expr_qualified_struct(&Some(expr.qself), &expr.strct); - } } } @@ -892,7 +807,7 @@ impl Printer { while let Expr::Block(expr) = body { if expr.attrs.is_empty() && expr.label.is_none() { let mut stmts = expr.block.stmts.iter(); - if let (Some(Stmt::Expr(inner)), None) = (stmts.next(), stmts.next()) { + if let (Some(Stmt::Expr(inner, None)), None) = (stmts.next(), stmts.next()) { body = inner; continue; } @@ -937,7 +852,7 @@ impl Printer { pre_break: Some('{'), ..BreakToken::default() }); - self.expr(body); + self.expr_beginning_of_line(body, true); self.scan_break(BreakToken { offset: -INDENT, pre_break: stmt::add_semi(body).then(|| ';'), @@ -950,26 +865,6 @@ impl Printer { } } - fn method_turbofish(&mut self, turbofish: &MethodTurbofish) { - self.word("::<"); - self.cbox(INDENT); - self.zerobreak(); - for arg in turbofish.args.iter().delimited() { - self.generic_method_argument(&arg); - self.trailing_comma(arg.is_last); - } - self.offset(-INDENT); - self.end(); - self.word(">"); - } - - fn generic_method_argument(&mut self, generic: &GenericMethodArgument) { - match generic { - GenericMethodArgument::Type(arg) => self.ty(arg), - GenericMethodArgument::Const(arg) => self.expr(arg), - } - } - fn call_args(&mut self, args: &Punctuated<Expr, Token![,]>) { let mut iter = args.iter(); match (iter.next(), iter.next()) { @@ -989,13 +884,13 @@ impl Printer { } } - fn small_block(&mut self, block: &Block, attrs: &[Attribute]) { + pub fn small_block(&mut self, block: &Block, attrs: &[Attribute]) { self.word("{"); if attr::has_inner(attrs) || !block.stmts.is_empty() { self.space(); self.inner_attrs(attrs); match (block.stmts.get(0), block.stmts.get(1)) { - (Some(Stmt::Expr(expr)), None) if stmt::break_after(expr) => { + (Some(Stmt::Expr(expr, None)), None) if stmt::break_after(expr) => { self.ibox(0); self.expr_beginning_of_line(expr, true); self.end(); @@ -1043,16 +938,18 @@ impl Printer { BinOp::Ne(_) => "!=", BinOp::Ge(_) => ">=", BinOp::Gt(_) => ">", - BinOp::AddEq(_) => "+=", - BinOp::SubEq(_) => "-=", - BinOp::MulEq(_) => "*=", - BinOp::DivEq(_) => "/=", - BinOp::RemEq(_) => "%=", - BinOp::BitXorEq(_) => "^=", - BinOp::BitAndEq(_) => "&=", - BinOp::BitOrEq(_) => "|=", - BinOp::ShlEq(_) => "<<=", - BinOp::ShrEq(_) => ">>=", + BinOp::AddAssign(_) => "+=", + BinOp::SubAssign(_) => "-=", + BinOp::MulAssign(_) => "*=", + BinOp::DivAssign(_) => "/=", + BinOp::RemAssign(_) => "%=", + BinOp::BitXorAssign(_) => "^=", + BinOp::BitAndAssign(_) => "&=", + BinOp::BitOrAssign(_) => "|=", + BinOp::ShlAssign(_) => "<<=", + BinOp::ShrAssign(_) => ">>=", + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown BinOp"), }); } @@ -1061,6 +958,8 @@ impl Printer { UnOp::Deref(_) => "*", UnOp::Not(_) => "!", UnOp::Neg(_) => "-", + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown UnOp"), }); } @@ -1073,17 +972,49 @@ impl Printer { } pub fn requires_terminator(expr: &Expr) -> bool { - // see https://github.com/rust-lang/rust/blob/2679c38fc/src/librustc_ast/util/classify.rs#L7-L25 + // see https://github.com/rust-lang/rust/blob/a266f1199/compiler/rustc_ast/src/util/classify.rs#L7-L26 match expr { - Expr::Unsafe(_) - | Expr::Block(_) - | Expr::If(_) + Expr::If(_) | Expr::Match(_) + | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc | Expr::While(_) | Expr::Loop(_) | Expr::ForLoop(_) + | Expr::TryBlock(_) + | Expr::Const(_) => false, + + Expr::Array(_) + | Expr::Assign(_) | Expr::Async(_) - | Expr::TryBlock(_) => false, + | Expr::Await(_) + | Expr::Binary(_) + | Expr::Break(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Closure(_) + | Expr::Continue(_) + | Expr::Field(_) + | Expr::Group(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Macro(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Struct(_) + | Expr::Try(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Verbatim(_) + | Expr::Yield(_) => true, + + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => true, } } @@ -1097,25 +1028,53 @@ fn contains_exterior_struct_lit(expr: &Expr) -> bool { Expr::Struct(_) => true, Expr::Assign(ExprAssign { left, right, .. }) - | Expr::AssignOp(ExprAssignOp { left, right, .. }) | Expr::Binary(ExprBinary { left, right, .. }) => { // X { y: 1 } + X { y: 2 } contains_exterior_struct_lit(left) || contains_exterior_struct_lit(right) } Expr::Await(ExprAwait { base: e, .. }) - | Expr::Box(ExprBox { expr: e, .. }) | Expr::Cast(ExprCast { expr: e, .. }) | Expr::Field(ExprField { base: e, .. }) | Expr::Index(ExprIndex { expr: e, .. }) | Expr::MethodCall(ExprMethodCall { receiver: e, .. }) | Expr::Reference(ExprReference { expr: e, .. }) - | Expr::Type(ExprType { expr: e, .. }) | Expr::Unary(ExprUnary { expr: e, .. }) => { // &X { y: 1 }, X { y: 1 }.y contains_exterior_struct_lit(e) } + Expr::Array(_) + | Expr::Async(_) + | Expr::Block(_) + | Expr::Break(_) + | Expr::Call(_) + | Expr::Closure(_) + | Expr::Const(_) + | Expr::Continue(_) + | Expr::ForLoop(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Try(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::While(_) + | Expr::Yield(_) => false, + + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => false, } } @@ -1127,15 +1086,17 @@ fn needs_newline_if_wrap(expr: &Expr) -> bool { | Expr::Block(_) | Expr::Break(ExprBreak { expr: None, .. }) | Expr::Closure(_) + | Expr::Const(_) | Expr::Continue(_) | Expr::ForLoop(_) | Expr::If(_) + | Expr::Infer(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::Path(_) - | Expr::Range(ExprRange { to: None, .. }) + | Expr::Range(ExprRange { end: None, .. }) | Expr::Repeat(_) | Expr::Return(ExprReturn { expr: None, .. }) | Expr::Struct(_) @@ -1147,22 +1108,19 @@ fn needs_newline_if_wrap(expr: &Expr) -> bool { | Expr::Yield(ExprYield { expr: None, .. }) => false, Expr::Assign(_) - | Expr::AssignOp(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Cast(_) | Expr::Field(_) | Expr::Index(_) - | Expr::MethodCall(_) - | Expr::Type(_) => true, + | Expr::MethodCall(_) => true, - Expr::Box(ExprBox { expr: e, .. }) - | Expr::Break(ExprBreak { expr: Some(e), .. }) + Expr::Break(ExprBreak { expr: Some(e), .. }) | Expr::Call(ExprCall { func: e, .. }) | Expr::Group(ExprGroup { expr: e, .. }) | Expr::Let(ExprLet { expr: e, .. }) | Expr::Paren(ExprParen { expr: e, .. }) - | Expr::Range(ExprRange { to: Some(e), .. }) + | Expr::Range(ExprRange { end: Some(e), .. }) | Expr::Reference(ExprReference { expr: e, .. }) | Expr::Return(ExprReturn { expr: Some(e), .. }) | Expr::Try(ExprTry { expr: e, .. }) @@ -1176,16 +1134,12 @@ fn needs_newline_if_wrap(expr: &Expr) -> bool { fn is_short_ident(expr: &Expr) -> bool { if let Expr::Path(expr) = expr { - if expr.attrs.is_empty() + return expr.attrs.is_empty() && expr.qself.is_none() - && expr.path.leading_colon.is_none() - && expr.path.segments.len() == 1 - && expr.path.segments[0].ident.to_string().len() as isize <= INDENT - { - if let PathArguments::None = expr.path.segments[0].arguments { - return true; - } - } + && expr + .path + .get_ident() + .map_or(false, |ident| ident.to_string().len() as isize <= INDENT); } false } @@ -1196,10 +1150,44 @@ fn is_blocklike(expr: &Expr) -> bool { | Expr::Async(ExprAsync { attrs, .. }) | Expr::Block(ExprBlock { attrs, .. }) | Expr::Closure(ExprClosure { attrs, .. }) + | Expr::Const(ExprConst { attrs, .. }) | Expr::Struct(ExprStruct { attrs, .. }) | Expr::TryBlock(ExprTryBlock { attrs, .. }) | Expr::Tuple(ExprTuple { attrs, .. }) | Expr::Unsafe(ExprUnsafe { attrs, .. }) => !attr::has_outer(attrs), + + Expr::Assign(_) + | Expr::Await(_) + | Expr::Binary(_) + | Expr::Break(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Continue(_) + | Expr::Field(_) + | Expr::ForLoop(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Try(_) + | Expr::Unary(_) + | Expr::Verbatim(_) + | Expr::While(_) + | Expr::Yield(_) => false, + + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => false, } } diff --git a/src/generics.rs b/src/generics.rs index ede53de..35da0b2 100644 --- a/src/generics.rs +++ b/src/generics.rs @@ -1,11 +1,13 @@ use crate::algorithm::Printer; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::INDENT; +use proc_macro2::TokenStream; use std::ptr; use syn::{ - BoundLifetimes, ConstParam, GenericParam, Generics, LifetimeDef, PredicateEq, - PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, - WhereClause, WherePredicate, + BoundLifetimes, ConstParam, GenericParam, Generics, LifetimeParam, PredicateLifetime, + PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, + WherePredicate, }; impl Printer { @@ -20,9 +22,6 @@ impl Printer { // Print lifetimes before types and consts, regardless of their // order in self.params. - // - // TODO: ordering rules for const parameters vs type parameters have - // not been settled yet. https://github.com/rust-lang/rust/issues/44580 #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, @@ -52,26 +51,26 @@ impl Printer { fn generic_param(&mut self, generic_param: &GenericParam) { match generic_param { GenericParam::Type(type_param) => self.type_param(type_param), - GenericParam::Lifetime(lifetime_def) => self.lifetime_def(lifetime_def), + GenericParam::Lifetime(lifetime_param) => self.lifetime_param(lifetime_param), GenericParam::Const(const_param) => self.const_param(const_param), } } pub fn bound_lifetimes(&mut self, bound_lifetimes: &BoundLifetimes) { self.word("for<"); - for lifetime_def in bound_lifetimes.lifetimes.iter().delimited() { - self.lifetime_def(&lifetime_def); - if !lifetime_def.is_last { + for param in bound_lifetimes.lifetimes.iter().delimited() { + self.generic_param(¶m); + if !param.is_last { self.word(", "); } } self.word("> "); } - fn lifetime_def(&mut self, lifetime_def: &LifetimeDef) { - self.outer_attrs(&lifetime_def.attrs); - self.lifetime(&lifetime_def.lifetime); - for lifetime in lifetime_def.bounds.iter().delimited() { + fn lifetime_param(&mut self, lifetime_param: &LifetimeParam) { + self.outer_attrs(&lifetime_param.attrs); + self.lifetime(&lifetime_param.lifetime); + for lifetime in lifetime_param.bounds.iter().delimited() { if lifetime.is_first { self.word(": "); } else { @@ -104,31 +103,33 @@ impl Printer { pub fn type_param_bound(&mut self, type_param_bound: &TypeParamBound) { match type_param_bound { - TypeParamBound::Trait(trait_bound) => self.trait_bound(trait_bound), + TypeParamBound::Trait(trait_bound) => { + let tilde_const = false; + self.trait_bound(trait_bound, tilde_const); + } TypeParamBound::Lifetime(lifetime) => self.lifetime(lifetime), + TypeParamBound::Verbatim(bound) => self.type_param_bound_verbatim(bound), + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown TypeParamBound"), } } - fn trait_bound(&mut self, trait_bound: &TraitBound) { + fn trait_bound(&mut self, trait_bound: &TraitBound, tilde_const: bool) { if trait_bound.paren_token.is_some() { self.word("("); } - let skip = match trait_bound.path.segments.first() { - Some(segment) if segment.ident == "const" => { - self.word("~const "); - 1 - } - _ => 0, - }; + if tilde_const { + self.word("~const "); + } self.trait_bound_modifier(&trait_bound.modifier); if let Some(bound_lifetimes) = &trait_bound.lifetimes { self.bound_lifetimes(bound_lifetimes); } - for segment in trait_bound.path.segments.iter().skip(skip).delimited() { + for segment in trait_bound.path.segments.iter().delimited() { if !segment.is_first || trait_bound.path.leading_colon.is_some() { self.word("::"); } - self.path_segment(&segment); + self.path_segment(&segment, PathKind::Type); } if trait_bound.paren_token.is_some() { self.word(")"); @@ -142,6 +143,49 @@ impl Printer { } } + #[cfg(not(feature = "verbatim"))] + fn type_param_bound_verbatim(&mut self, bound: &TokenStream) { + unimplemented!("TypeParamBound::Verbatim `{}`", bound); + } + + #[cfg(feature = "verbatim")] + fn type_param_bound_verbatim(&mut self, tokens: &TokenStream) { + use syn::parse::{Parse, ParseStream, Result}; + use syn::{parenthesized, token, Token}; + + enum TypeParamBoundVerbatim { + TildeConst(TraitBound), + } + + impl Parse for TypeParamBoundVerbatim { + fn parse(input: ParseStream) -> Result<Self> { + let content; + let (paren_token, content) = if input.peek(token::Paren) { + (Some(parenthesized!(content in input)), &content) + } else { + (None, input) + }; + content.parse::<Token![~]>()?; + content.parse::<Token![const]>()?; + let mut bound: TraitBound = content.parse()?; + bound.paren_token = paren_token; + Ok(TypeParamBoundVerbatim::TildeConst(bound)) + } + } + + let bound: TypeParamBoundVerbatim = match syn::parse2(tokens.clone()) { + Ok(bound) => bound, + Err(_) => unimplemented!("TypeParamBound::Verbatim `{}`", tokens), + }; + + match bound { + TypeParamBoundVerbatim::TildeConst(trait_bound) => { + let tilde_const = true; + self.trait_bound(&trait_bound, tilde_const); + } + } + } + fn const_param(&mut self, const_param: &ConstParam) { self.outer_attrs(&const_param.attrs); self.word("const "); @@ -235,7 +279,8 @@ impl Printer { match predicate { WherePredicate::Type(predicate) => self.predicate_type(predicate), WherePredicate::Lifetime(predicate) => self.predicate_lifetime(predicate), - WherePredicate::Eq(predicate) => self.predicate_eq(predicate), + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown WherePredicate"), } } @@ -277,10 +322,4 @@ impl Printer { } self.end(); } - - fn predicate_eq(&mut self, predicate: &PredicateEq) { - self.ty(&predicate.lhs_ty); - self.word(" = "); - self.ty(&predicate.rhs_ty); - } } diff --git a/src/item.rs b/src/item.rs index 62252f1..88d0c04 100644 --- a/src/item.rs +++ b/src/item.rs @@ -1,14 +1,15 @@ use crate::algorithm::Printer; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, - ForeignItemType, ImplItem, ImplItemConst, ImplItemMacro, ImplItemMethod, ImplItemType, Item, - ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMacro2, - ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Pat, - Receiver, Signature, Stmt, TraitItem, TraitItemConst, TraitItemMacro, TraitItemMethod, - TraitItemType, Type, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, + ForeignItemType, ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, Item, + ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, + ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, + Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, + TraitItemType, Type, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic, }; impl Printer { @@ -21,7 +22,6 @@ impl Printer { Item::ForeignMod(item) => self.item_foreign_mod(item), Item::Impl(item) => self.item_impl(item), Item::Macro(item) => self.item_macro(item), - Item::Macro2(item) => self.item_macro2(item), Item::Mod(item) => self.item_mod(item), Item::Static(item) => self.item_static(item), Item::Struct(item) => self.item_struct(item), @@ -42,6 +42,7 @@ impl Printer { self.visibility(&item.vis); self.word("const "); self.ident(&item.ident); + self.generics(&item.generics); self.word(": "); self.ty(&item.ty); self.word(" = "); @@ -107,6 +108,9 @@ impl Printer { fn item_foreign_mod(&mut self, item: &ItemForeignMod) { self.outer_attrs(&item.attrs); self.cbox(INDENT); + if item.unsafety.is_some() { + self.word("unsafe "); + } self.abi(&item.abi); self.word("{"); self.hardbreak_if_nonempty(); @@ -139,7 +143,7 @@ impl Printer { if negative_polarity.is_some() { self.word("!"); } - self.path(path); + self.path(path, PathKind::Type); self.space(); self.word("for "); } @@ -160,19 +164,18 @@ impl Printer { fn item_macro(&mut self, item: &ItemMacro) { self.outer_attrs(&item.attrs); - self.mac(&item.mac, item.ident.as_ref()); - self.mac_semi_if_needed(&item.mac.delimiter); + let semicolon = true; + self.mac(&item.mac, item.ident.as_ref(), semicolon); self.hardbreak(); } - fn item_macro2(&mut self, item: &ItemMacro2) { - unimplemented!("Item::Macro2 `macro {} {}`", item.ident, item.rules); - } - fn item_mod(&mut self, item: &ItemMod) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); + if item.unsafety.is_some() { + self.word("unsafe "); + } self.word("mod "); self.ident(&item.ident); if let Some((_brace, items)) = &item.content { @@ -197,9 +200,7 @@ impl Printer { self.cbox(0); self.visibility(&item.vis); self.word("static "); - if item.mutability.is_some() { - self.word("mut "); - } + self.static_mutability(&item.mutability); self.ident(&item.ident); self.word(": "); self.ty(&item.ty); @@ -362,25 +363,202 @@ impl Printer { #[cfg(feature = "verbatim")] fn item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; - use syn::{Attribute, Token}; + use syn::punctuated::Punctuated; + use syn::{ + braced, parenthesized, token, Attribute, Generics, Ident, Lifetime, Token, Visibility, + }; + use verbatim::{ + FlexibleItemConst, FlexibleItemFn, FlexibleItemStatic, FlexibleItemType, + WhereClauseLocation, + }; enum ItemVerbatim { Empty, - UnsafeForeignMod(ItemForeignMod), + ConstFlexible(FlexibleItemConst), + FnFlexible(FlexibleItemFn), + ImplFlexible(ImplFlexible), + Macro2(Macro2), + StaticFlexible(FlexibleItemStatic), + TypeFlexible(FlexibleItemType), + UseBrace(UseBrace), + } + + struct ImplFlexible { + attrs: Vec<Attribute>, + vis: Visibility, + defaultness: bool, + unsafety: bool, + generics: Generics, + constness: ImplConstness, + negative_impl: bool, + trait_: Option<Type>, + self_ty: Type, + items: Vec<ImplItem>, + } + + enum ImplConstness { + None, + MaybeConst, + Const, + } + + struct Macro2 { + attrs: Vec<Attribute>, + vis: Visibility, + ident: Ident, + args: Option<TokenStream>, + body: TokenStream, + } + + struct UseBrace { + attrs: Vec<Attribute>, + vis: Visibility, + trees: Punctuated<RootUseTree, Token![,]>, + } + + struct RootUseTree { + leading_colon: Option<Token![::]>, + inner: UseTree, + } + + impl Parse for ImplConstness { + fn parse(input: ParseStream) -> Result<Self> { + if input.parse::<Option<Token![?]>>()?.is_some() { + input.parse::<Token![const]>()?; + Ok(ImplConstness::MaybeConst) + } else if input.parse::<Option<Token![const]>>()?.is_some() { + Ok(ImplConstness::Const) + } else { + Ok(ImplConstness::None) + } + } + } + + impl Parse for RootUseTree { + fn parse(input: ParseStream) -> Result<Self> { + Ok(RootUseTree { + leading_colon: input.parse()?, + inner: input.parse()?, + }) + } } impl Parse for ItemVerbatim { fn parse(input: ParseStream) -> Result<Self> { if input.is_empty() { - Ok(ItemVerbatim::Empty) - } else { - let attrs = input.call(Attribute::parse_outer)?; - input.parse::<Token![unsafe]>()?; - let module: ItemForeignMod = input.parse()?; - Ok(ItemVerbatim::UnsafeForeignMod(ItemForeignMod { + return Ok(ItemVerbatim::Empty); + } + + let mut attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![const]) && (input.peek2(Ident) || input.peek2(Token![_])) { + let defaultness = false; + let flexible_item = FlexibleItemConst::parse(attrs, vis, defaultness, input)?; + Ok(ItemVerbatim::ConstFlexible(flexible_item)) + } else if input.peek(Token![const]) + || lookahead.peek(Token![async]) + || lookahead.peek(Token![unsafe]) && !input.peek2(Token![impl]) + || lookahead.peek(Token![extern]) + || lookahead.peek(Token![fn]) + { + let defaultness = false; + let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; + Ok(ItemVerbatim::FnFlexible(flexible_item)) + } else if lookahead.peek(Token![default]) + || input.peek(Token![unsafe]) + || lookahead.peek(Token![impl]) + { + let defaultness = input.parse::<Option<Token![default]>>()?.is_some(); + let unsafety = input.parse::<Option<Token![unsafe]>>()?.is_some(); + input.parse::<Token![impl]>()?; + let has_generics = input.peek(Token![<]) + && (input.peek2(Token![>]) + || input.peek2(Token![#]) + || (input.peek2(Ident) || input.peek2(Lifetime)) + && (input.peek3(Token![:]) + || input.peek3(Token![,]) + || input.peek3(Token![>]) + || input.peek3(Token![=])) + || input.peek2(Token![const])); + let mut generics: Generics = if has_generics { + input.parse()? + } else { + Generics::default() + }; + let constness: ImplConstness = input.parse()?; + let negative_impl = + !input.peek2(token::Brace) && input.parse::<Option<Token![!]>>()?.is_some(); + let first_ty: Type = input.parse()?; + let (trait_, self_ty) = if input.parse::<Option<Token![for]>>()?.is_some() { + (Some(first_ty), input.parse()?) + } else { + (None, first_ty) + }; + generics.where_clause = input.parse()?; + let content; + braced!(content in input); + let inner_attrs = content.call(Attribute::parse_inner)?; + attrs.extend(inner_attrs); + let mut items = Vec::new(); + while !content.is_empty() { + items.push(content.parse()?); + } + Ok(ItemVerbatim::ImplFlexible(ImplFlexible { + attrs, + vis, + defaultness, + unsafety, + generics, + constness, + negative_impl, + trait_, + self_ty, + items, + })) + } else if lookahead.peek(Token![macro]) { + input.parse::<Token![macro]>()?; + let ident: Ident = input.parse()?; + let args = if input.peek(token::Paren) { + let paren_content; + parenthesized!(paren_content in input); + Some(paren_content.parse::<TokenStream>()?) + } else { + None + }; + let brace_content; + braced!(brace_content in input); + let body: TokenStream = brace_content.parse()?; + Ok(ItemVerbatim::Macro2(Macro2 { attrs, - ..module + vis, + ident, + args, + body, })) + } else if lookahead.peek(Token![static]) { + let flexible_item = FlexibleItemStatic::parse(attrs, vis, input)?; + Ok(ItemVerbatim::StaticFlexible(flexible_item)) + } else if lookahead.peek(Token![type]) { + let defaultness = false; + let flexible_item = FlexibleItemType::parse( + attrs, + vis, + defaultness, + input, + WhereClauseLocation::BeforeEq, + )?; + Ok(ItemVerbatim::TypeFlexible(flexible_item)) + } else if lookahead.peek(Token![use]) { + input.parse::<Token![use]>()?; + let content; + braced!(content in input); + let trees = content.parse_terminated(RootUseTree::parse, Token![,])?; + input.parse::<Token![;]>()?; + Ok(ItemVerbatim::UseBrace(UseBrace { attrs, vis, trees })) + } else { + Err(lookahead.error()) } } } @@ -391,25 +569,136 @@ impl Printer { }; match item { - ItemVerbatim::Empty => {} - ItemVerbatim::UnsafeForeignMod(item) => { + ItemVerbatim::Empty => { + self.hardbreak(); + } + ItemVerbatim::ConstFlexible(item) => { + self.flexible_item_const(&item); + } + ItemVerbatim::FnFlexible(item) => { + self.flexible_item_fn(&item); + } + ItemVerbatim::ImplFlexible(item) => { self.outer_attrs(&item.attrs); self.cbox(INDENT); - self.word("unsafe "); - self.abi(&item.abi); + self.ibox(-INDENT); + self.cbox(INDENT); + self.visibility(&item.vis); + if item.defaultness { + self.word("default "); + } + if item.unsafety { + self.word("unsafe "); + } + self.word("impl"); + self.generics(&item.generics); + self.end(); + self.nbsp(); + match item.constness { + ImplConstness::None => {} + ImplConstness::MaybeConst => self.word("?const "), + ImplConstness::Const => self.word("const "), + } + if item.negative_impl { + self.word("!"); + } + if let Some(trait_) = &item.trait_ { + self.ty(trait_); + self.space(); + self.word("for "); + } + self.ty(&item.self_ty); + self.end(); + self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); - for foreign_item in &item.items { - self.foreign_item(foreign_item); + for impl_item in &item.items { + self.impl_item(impl_item); } self.offset(-INDENT); self.end(); self.word("}"); + self.hardbreak(); + } + ItemVerbatim::Macro2(item) => { + self.outer_attrs(&item.attrs); + self.visibility(&item.vis); + self.word("macro "); + self.ident(&item.ident); + if let Some(args) = &item.args { + self.word("("); + self.cbox(INDENT); + self.zerobreak(); + self.ibox(0); + self.macro_rules_tokens(args.clone(), true); + self.end(); + self.zerobreak(); + self.offset(-INDENT); + self.end(); + self.word(")"); + } + self.word(" {"); + if !item.body.is_empty() { + self.neverbreak(); + self.cbox(INDENT); + self.hardbreak(); + self.ibox(0); + self.macro_rules_tokens(item.body.clone(), false); + self.end(); + self.hardbreak(); + self.offset(-INDENT); + self.end(); + } + self.word("}"); + self.hardbreak(); + } + ItemVerbatim::StaticFlexible(item) => { + self.flexible_item_static(&item); + } + ItemVerbatim::TypeFlexible(item) => { + self.flexible_item_type(&item); + } + ItemVerbatim::UseBrace(item) => { + self.outer_attrs(&item.attrs); + self.visibility(&item.vis); + self.word("use "); + if item.trees.len() == 1 { + self.word("::"); + self.use_tree(&item.trees[0].inner); + } else { + self.cbox(INDENT); + self.word("{"); + self.zerobreak(); + self.ibox(0); + for use_tree in item.trees.iter().delimited() { + if use_tree.leading_colon.is_some() { + self.word("::"); + } + self.use_tree(&use_tree.inner); + if !use_tree.is_last { + self.word(","); + let mut use_tree = &use_tree.inner; + while let UseTree::Path(use_path) = use_tree { + use_tree = &use_path.tree; + } + if let UseTree::Group(_) = use_tree { + self.hardbreak(); + } else { + self.space(); + } + } + } + self.end(); + self.trailing_comma(true); + self.offset(-INDENT); + self.word("}"); + self.end(); + } + self.word(";"); + self.hardbreak(); } } - - self.hardbreak(); } fn use_tree(&mut self, use_tree: &UseTree) { @@ -503,9 +792,7 @@ impl Printer { self.cbox(0); self.visibility(&foreign_item.vis); self.word("static "); - if foreign_item.mutability.is_some() { - self.word("mut "); - } + self.static_mutability(&foreign_item.mutability); self.ident(&foreign_item.ident); self.word(": "); self.ty(&foreign_item.ty); @@ -520,6 +807,7 @@ impl Printer { self.visibility(&foreign_item.vis); self.word("type "); self.ident(&foreign_item.ident); + self.generics(&foreign_item.generics); self.word(";"); self.end(); self.hardbreak(); @@ -527,8 +815,8 @@ impl Printer { fn foreign_item_macro(&mut self, foreign_item: &ForeignItemMacro) { self.outer_attrs(&foreign_item.attrs); - self.mac(&foreign_item.mac, None); - self.mac_semi_if_needed(&foreign_item.mac.delimiter); + let semicolon = true; + self.mac(&foreign_item.mac, None, semicolon); self.hardbreak(); } @@ -543,14 +831,50 @@ impl Printer { #[cfg(feature = "verbatim")] fn foreign_item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; + use syn::{Attribute, Token, Visibility}; + use verbatim::{FlexibleItemFn, FlexibleItemStatic, FlexibleItemType, WhereClauseLocation}; enum ForeignItemVerbatim { - TypeAlias(ItemType), + Empty, + FnFlexible(FlexibleItemFn), + StaticFlexible(FlexibleItemStatic), + TypeFlexible(FlexibleItemType), } impl Parse for ForeignItemVerbatim { fn parse(input: ParseStream) -> Result<Self> { - input.parse().map(ForeignItemVerbatim::TypeAlias) + if input.is_empty() { + return Ok(ForeignItemVerbatim::Empty); + } + + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let defaultness = false; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![const]) + || lookahead.peek(Token![async]) + || lookahead.peek(Token![unsafe]) + || lookahead.peek(Token![extern]) + || lookahead.peek(Token![fn]) + { + let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; + Ok(ForeignItemVerbatim::FnFlexible(flexible_item)) + } else if lookahead.peek(Token![static]) { + let flexible_item = FlexibleItemStatic::parse(attrs, vis, input)?; + Ok(ForeignItemVerbatim::StaticFlexible(flexible_item)) + } else if lookahead.peek(Token![type]) { + let flexible_item = FlexibleItemType::parse( + attrs, + vis, + defaultness, + input, + WhereClauseLocation::Both, + )?; + Ok(ForeignItemVerbatim::TypeFlexible(flexible_item)) + } else { + Err(lookahead.error()) + } } } @@ -560,14 +884,25 @@ impl Printer { }; match foreign_item { - ForeignItemVerbatim::TypeAlias(item) => self.item_type(&item), + ForeignItemVerbatim::Empty => { + self.hardbreak(); + } + ForeignItemVerbatim::FnFlexible(foreign_item) => { + self.flexible_item_fn(&foreign_item); + } + ForeignItemVerbatim::StaticFlexible(foreign_item) => { + self.flexible_item_static(&foreign_item); + } + ForeignItemVerbatim::TypeFlexible(foreign_item) => { + self.flexible_item_type(&foreign_item); + } } } fn trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Const(item) => self.trait_item_const(item), - TraitItem::Method(item) => self.trait_item_method(item), + TraitItem::Fn(item) => self.trait_item_fn(item), TraitItem::Type(item) => self.trait_item_type(item), TraitItem::Macro(item) => self.trait_item_macro(item), TraitItem::Verbatim(item) => self.trait_item_verbatim(item), @@ -581,6 +916,7 @@ impl Printer { self.cbox(0); self.word("const "); self.ident(&trait_item.ident); + self.generics(&trait_item.generics); self.word(": "); self.ty(&trait_item.ty); if let Some((_eq_token, default)) = &trait_item.default { @@ -593,7 +929,7 @@ impl Printer { self.hardbreak(); } - fn trait_item_method(&mut self, trait_item: &TraitItemMethod) { + fn trait_item_fn(&mut self, trait_item: &TraitItemFn) { self.outer_attrs(&trait_item.attrs); self.cbox(INDENT); self.signature(&trait_item.sig); @@ -633,7 +969,9 @@ impl Printer { if let Some((_eq_token, default)) = &trait_item.default { self.word(" = "); self.neverbreak(); + self.ibox(-INDENT); self.ty(default); + self.end(); } self.where_clause_oneline_semi(&trait_item.generics.where_clause); self.end(); @@ -642,11 +980,12 @@ impl Printer { fn trait_item_macro(&mut self, trait_item: &TraitItemMacro) { self.outer_attrs(&trait_item.attrs); - self.mac(&trait_item.mac, None); - self.mac_semi_if_needed(&trait_item.mac.delimiter); + let semicolon = true; + self.mac(&trait_item.mac, None, semicolon); self.hardbreak(); } + #[cfg(not(feature = "verbatim"))] fn trait_item_verbatim(&mut self, trait_item: &TokenStream) { if !trait_item.is_empty() { unimplemented!("TraitItem::Verbatim `{}`", trait_item); @@ -654,10 +993,91 @@ impl Printer { self.hardbreak(); } + #[cfg(feature = "verbatim")] + fn trait_item_verbatim(&mut self, tokens: &TokenStream) { + use syn::parse::{Parse, ParseStream, Result}; + use syn::{Attribute, Token, Visibility}; + use verbatim::{FlexibleItemType, WhereClauseLocation}; + + enum TraitItemVerbatim { + Empty, + TypeFlexible(FlexibleItemType), + PubOrDefault(PubOrDefaultTraitItem), + } + + struct PubOrDefaultTraitItem { + attrs: Vec<Attribute>, + vis: Visibility, + defaultness: bool, + trait_item: TraitItem, + } + + impl Parse for TraitItemVerbatim { + fn parse(input: ParseStream) -> Result<Self> { + if input.is_empty() { + return Ok(TraitItemVerbatim::Empty); + } + + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let defaultness = input.parse::<Option<Token![default]>>()?.is_some(); + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![type]) { + let flexible_item = FlexibleItemType::parse( + attrs, + vis, + defaultness, + input, + WhereClauseLocation::AfterEq, + )?; + Ok(TraitItemVerbatim::TypeFlexible(flexible_item)) + } else if (lookahead.peek(Token![const]) + || lookahead.peek(Token![async]) + || lookahead.peek(Token![unsafe]) + || lookahead.peek(Token![extern]) + || lookahead.peek(Token![fn])) + && (!matches!(vis, Visibility::Inherited) || defaultness) + { + Ok(TraitItemVerbatim::PubOrDefault(PubOrDefaultTraitItem { + attrs, + vis, + defaultness, + trait_item: input.parse()?, + })) + } else { + Err(lookahead.error()) + } + } + } + + let impl_item: TraitItemVerbatim = match syn::parse2(tokens.clone()) { + Ok(impl_item) => impl_item, + Err(_) => unimplemented!("TraitItem::Verbatim `{}`", tokens), + }; + + match impl_item { + TraitItemVerbatim::Empty => { + self.hardbreak(); + } + TraitItemVerbatim::TypeFlexible(trait_item) => { + self.flexible_item_type(&trait_item); + } + TraitItemVerbatim::PubOrDefault(trait_item) => { + self.outer_attrs(&trait_item.attrs); + self.visibility(&trait_item.vis); + if trait_item.defaultness { + self.word("default "); + } + self.trait_item(&trait_item.trait_item); + } + } + } + fn impl_item(&mut self, impl_item: &ImplItem) { match impl_item { ImplItem::Const(item) => self.impl_item_const(item), - ImplItem::Method(item) => self.impl_item_method(item), + ImplItem::Fn(item) => self.impl_item_fn(item), ImplItem::Type(item) => self.impl_item_type(item), ImplItem::Macro(item) => self.impl_item_macro(item), ImplItem::Verbatim(item) => self.impl_item_verbatim(item), @@ -675,6 +1095,7 @@ impl Printer { } self.word("const "); self.ident(&impl_item.ident); + self.generics(&impl_item.generics); self.word(": "); self.ty(&impl_item.ty); self.word(" = "); @@ -685,7 +1106,7 @@ impl Printer { self.hardbreak(); } - fn impl_item_method(&mut self, impl_item: &ImplItemMethod) { + fn impl_item_fn(&mut self, impl_item: &ImplItemFn) { self.outer_attrs(&impl_item.attrs); self.cbox(INDENT); self.visibility(&impl_item.vis); @@ -693,16 +1114,6 @@ impl Printer { self.word("default "); } self.signature(&impl_item.sig); - if impl_item.block.stmts.len() == 1 { - if let Stmt::Item(Item::Verbatim(verbatim)) = &impl_item.block.stmts[0] { - if verbatim.to_string() == ";" { - self.where_clause_semi(&impl_item.sig.generics.where_clause); - self.end(); - self.hardbreak(); - return; - } - } - } self.where_clause_for_body(&impl_item.sig.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); @@ -738,11 +1149,12 @@ impl Printer { fn impl_item_macro(&mut self, impl_item: &ImplItemMacro) { self.outer_attrs(&impl_item.attrs); - self.mac(&impl_item.mac, None); - self.mac_semi_if_needed(&impl_item.mac.delimiter); + let semicolon = true; + self.mac(&impl_item.mac, None, semicolon); self.hardbreak(); } + #[cfg(not(feature = "verbatim"))] fn impl_item_verbatim(&mut self, impl_item: &TokenStream) { if !impl_item.is_empty() { unimplemented!("ImplItem::Verbatim `{}`", impl_item); @@ -750,29 +1162,73 @@ impl Printer { self.hardbreak(); } - fn maybe_variadic(&mut self, arg: &FnArg) -> bool { - let pat_type = match arg { - FnArg::Typed(pat_type) => pat_type, - FnArg::Receiver(receiver) => { - self.receiver(receiver); - return false; + #[cfg(feature = "verbatim")] + fn impl_item_verbatim(&mut self, tokens: &TokenStream) { + use syn::parse::{Parse, ParseStream, Result}; + use syn::{Attribute, Ident, Token, Visibility}; + use verbatim::{FlexibleItemConst, FlexibleItemFn, FlexibleItemType, WhereClauseLocation}; + + enum ImplItemVerbatim { + Empty, + ConstFlexible(FlexibleItemConst), + FnFlexible(FlexibleItemFn), + TypeFlexible(FlexibleItemType), + } + + impl Parse for ImplItemVerbatim { + fn parse(input: ParseStream) -> Result<Self> { + if input.is_empty() { + return Ok(ImplItemVerbatim::Empty); + } + + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let defaultness = input.parse::<Option<Token![default]>>()?.is_some(); + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![const]) && (input.peek2(Ident) || input.peek2(Token![_])) { + let flexible_item = FlexibleItemConst::parse(attrs, vis, defaultness, input)?; + Ok(ImplItemVerbatim::ConstFlexible(flexible_item)) + } else if input.peek(Token![const]) + || lookahead.peek(Token![async]) + || lookahead.peek(Token![unsafe]) + || lookahead.peek(Token![extern]) + || lookahead.peek(Token![fn]) + { + let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; + Ok(ImplItemVerbatim::FnFlexible(flexible_item)) + } else if lookahead.peek(Token![type]) { + let flexible_item = FlexibleItemType::parse( + attrs, + vis, + defaultness, + input, + WhereClauseLocation::AfterEq, + )?; + Ok(ImplItemVerbatim::TypeFlexible(flexible_item)) + } else { + Err(lookahead.error()) + } } + } + + let impl_item: ImplItemVerbatim = match syn::parse2(tokens.clone()) { + Ok(impl_item) => impl_item, + Err(_) => unimplemented!("ImplItem::Verbatim `{}`", tokens), }; - match pat_type.ty.as_ref() { - Type::Verbatim(ty) if ty.to_string() == "..." => { - match pat_type.pat.as_ref() { - Pat::Verbatim(pat) if pat.to_string() == "..." => { - self.outer_attrs(&pat_type.attrs); - self.word("..."); - } - _ => self.pat_type(pat_type), - } - true + match impl_item { + ImplItemVerbatim::Empty => { + self.hardbreak(); + } + ImplItemVerbatim::ConstFlexible(impl_item) => { + self.flexible_item_const(&impl_item); } - _ => { - self.pat_type(pat_type); - false + ImplItemVerbatim::FnFlexible(impl_item) => { + self.flexible_item_fn(&impl_item); + } + ImplItemVerbatim::TypeFlexible(impl_item) => { + self.flexible_item_type(&impl_item); } } } @@ -797,18 +1253,13 @@ impl Printer { self.neverbreak(); self.cbox(0); self.zerobreak(); - let mut last_is_variadic = false; for input in signature.inputs.iter().delimited() { - last_is_variadic = self.maybe_variadic(&input); - if last_is_variadic { - self.zerobreak(); - } else { - let is_last = input.is_last && signature.variadic.is_none(); - self.trailing_comma(is_last); - } + self.fn_arg(&input); + let is_last = input.is_last && signature.variadic.is_none(); + self.trailing_comma(is_last); } - if signature.variadic.is_some() && !last_is_variadic { - self.word("..."); + if let Some(variadic) = &signature.variadic { + self.variadic(variadic); self.zerobreak(); } self.offset(-INDENT); @@ -819,6 +1270,13 @@ impl Printer { self.end(); } + fn fn_arg(&mut self, fn_arg: &FnArg) { + match fn_arg { + FnArg::Receiver(receiver) => self.receiver(receiver), + FnArg::Typed(pat_type) => self.pat_type(pat_type), + } + } + fn receiver(&mut self, receiver: &Receiver) { self.outer_attrs(&receiver.attrs); if let Some((_ampersand, lifetime)) = &receiver.reference { @@ -832,5 +1290,357 @@ impl Printer { self.word("mut "); } self.word("self"); + if receiver.colon_token.is_some() { + self.word(": "); + self.ty(&receiver.ty); + } else { + let consistent = match (&receiver.reference, &receiver.mutability, &*receiver.ty) { + (Some(_), mutability, Type::Reference(ty)) => { + mutability.is_some() == ty.mutability.is_some() + && match &*ty.elem { + Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"), + _ => false, + } + } + (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"), + _ => false, + }; + if !consistent { + self.word(": "); + self.ty(&receiver.ty); + } + } + } + + fn variadic(&mut self, variadic: &Variadic) { + self.outer_attrs(&variadic.attrs); + if let Some((pat, _colon)) = &variadic.pat { + self.pat(pat); + self.word(": "); + } + self.word("..."); + } + + fn static_mutability(&mut self, mutability: &StaticMutability) { + match mutability { + StaticMutability::Mut(_) => self.word("mut "), + StaticMutability::None => {} + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown StaticMutability"), + } + } +} + +#[cfg(feature = "verbatim")] +mod verbatim { + use crate::algorithm::Printer; + use crate::iter::IterDelimited; + use crate::INDENT; + use syn::ext::IdentExt; + use syn::parse::{ParseStream, Result}; + use syn::{ + braced, token, Attribute, Block, Expr, Generics, Ident, Signature, StaticMutability, Stmt, + Token, Type, TypeParamBound, Visibility, WhereClause, + }; + + pub struct FlexibleItemConst { + pub attrs: Vec<Attribute>, + pub vis: Visibility, + pub defaultness: bool, + pub ident: Ident, + pub ty: Type, + } + + pub struct FlexibleItemFn { + pub attrs: Vec<Attribute>, + pub vis: Visibility, + pub defaultness: bool, + pub sig: Signature, + pub body: Option<Vec<Stmt>>, + } + + pub struct FlexibleItemStatic { + pub attrs: Vec<Attribute>, + pub vis: Visibility, + pub mutability: StaticMutability, + pub ident: Ident, + pub ty: Option<Type>, + pub expr: Option<Expr>, + } + + pub struct FlexibleItemType { + pub attrs: Vec<Attribute>, + pub vis: Visibility, + pub defaultness: bool, + pub ident: Ident, + pub generics: Generics, + pub bounds: Vec<TypeParamBound>, + pub definition: Option<Type>, + pub where_clause_after_eq: Option<WhereClause>, + } + + pub enum WhereClauseLocation { + // type Ty<T> where T: 'static = T; + BeforeEq, + // type Ty<T> = T where T: 'static; + AfterEq, + // TODO: goes away once the migration period on rust-lang/rust#89122 is over + Both, + } + + impl FlexibleItemConst { + pub fn parse( + attrs: Vec<Attribute>, + vis: Visibility, + defaultness: bool, + input: ParseStream, + ) -> Result<Self> { + input.parse::<Token![const]>()?; + let ident = input.call(Ident::parse_any)?; + input.parse::<Token![:]>()?; + let ty: Type = input.parse()?; + input.parse::<Token![;]>()?; + + Ok(FlexibleItemConst { + attrs, + vis, + defaultness, + ident, + ty, + }) + } + } + + impl FlexibleItemFn { + pub fn parse( + mut attrs: Vec<Attribute>, + vis: Visibility, + defaultness: bool, + input: ParseStream, + ) -> Result<Self> { + let sig: Signature = input.parse()?; + + let lookahead = input.lookahead1(); + let body = if lookahead.peek(Token![;]) { + input.parse::<Token![;]>()?; + None + } else if lookahead.peek(token::Brace) { + let content; + braced!(content in input); + attrs.extend(content.call(Attribute::parse_inner)?); + Some(content.call(Block::parse_within)?) + } else { + return Err(lookahead.error()); + }; + + Ok(FlexibleItemFn { + attrs, + vis, + defaultness, + sig, + body, + }) + } + } + + impl FlexibleItemStatic { + pub fn parse(attrs: Vec<Attribute>, vis: Visibility, input: ParseStream) -> Result<Self> { + input.parse::<Token![static]>()?; + let mutability: StaticMutability = input.parse()?; + let ident = input.parse()?; + + let lookahead = input.lookahead1(); + let has_type = lookahead.peek(Token![:]); + let has_expr = lookahead.peek(Token![=]); + if !has_type && !has_expr { + return Err(lookahead.error()); + } + + let ty: Option<Type> = if has_type { + input.parse::<Token![:]>()?; + input.parse().map(Some)? + } else { + None + }; + + let expr: Option<Expr> = if input.parse::<Option<Token![=]>>()?.is_some() { + input.parse().map(Some)? + } else { + None + }; + + input.parse::<Token![;]>()?; + + Ok(FlexibleItemStatic { + attrs, + vis, + mutability, + ident, + ty, + expr, + }) + } + } + + impl FlexibleItemType { + pub fn parse( + attrs: Vec<Attribute>, + vis: Visibility, + defaultness: bool, + input: ParseStream, + where_clause_location: WhereClauseLocation, + ) -> Result<Self> { + input.parse::<Token![type]>()?; + let ident: Ident = input.parse()?; + let mut generics: Generics = input.parse()?; + + let mut bounds = Vec::new(); + if input.parse::<Option<Token![:]>>()?.is_some() { + loop { + if input.peek(Token![where]) || input.peek(Token![=]) || input.peek(Token![;]) { + break; + } + bounds.push(input.parse::<TypeParamBound>()?); + if input.peek(Token![where]) || input.peek(Token![=]) || input.peek(Token![;]) { + break; + } + input.parse::<Token![+]>()?; + } + } + + match where_clause_location { + WhereClauseLocation::BeforeEq | WhereClauseLocation::Both => { + generics.where_clause = input.parse()?; + } + WhereClauseLocation::AfterEq => {} + } + + let definition = if input.parse::<Option<Token![=]>>()?.is_some() { + Some(input.parse()?) + } else { + None + }; + + let where_clause_after_eq = match where_clause_location { + WhereClauseLocation::AfterEq | WhereClauseLocation::Both + if generics.where_clause.is_none() => + { + input.parse()? + } + _ => None, + }; + + input.parse::<Token![;]>()?; + + Ok(FlexibleItemType { + attrs, + vis, + defaultness, + ident, + generics, + bounds, + definition, + where_clause_after_eq, + }) + } + } + + impl Printer { + pub fn flexible_item_const(&mut self, item: &FlexibleItemConst) { + self.outer_attrs(&item.attrs); + self.cbox(0); + self.visibility(&item.vis); + if item.defaultness { + self.word("default "); + } + self.word("const "); + self.ident(&item.ident); + self.word(": "); + self.ty(&item.ty); + self.word(";"); + self.end(); + self.hardbreak(); + } + + pub fn flexible_item_fn(&mut self, item: &FlexibleItemFn) { + self.outer_attrs(&item.attrs); + self.cbox(INDENT); + self.visibility(&item.vis); + if item.defaultness { + self.word("default "); + } + self.signature(&item.sig); + if let Some(body) = &item.body { + self.where_clause_for_body(&item.sig.generics.where_clause); + self.word("{"); + self.hardbreak_if_nonempty(); + self.inner_attrs(&item.attrs); + for stmt in body { + self.stmt(stmt); + } + self.offset(-INDENT); + self.end(); + self.word("}"); + } else { + self.where_clause_semi(&item.sig.generics.where_clause); + self.end(); + } + self.hardbreak(); + } + + pub fn flexible_item_static(&mut self, item: &FlexibleItemStatic) { + self.outer_attrs(&item.attrs); + self.cbox(0); + self.visibility(&item.vis); + self.word("static "); + self.static_mutability(&item.mutability); + self.ident(&item.ident); + if let Some(ty) = &item.ty { + self.word(": "); + self.ty(ty); + } + if let Some(expr) = &item.expr { + self.word(" = "); + self.neverbreak(); + self.expr(expr); + } + self.word(";"); + self.end(); + self.hardbreak(); + } + + pub fn flexible_item_type(&mut self, item: &FlexibleItemType) { + self.outer_attrs(&item.attrs); + self.cbox(INDENT); + self.visibility(&item.vis); + if item.defaultness { + self.word("default "); + } + self.word("type "); + self.ident(&item.ident); + self.generics(&item.generics); + for bound in item.bounds.iter().delimited() { + if bound.is_first { + self.word(": "); + } else { + self.space(); + self.word("+ "); + } + self.type_param_bound(&bound); + } + if let Some(definition) = &item.definition { + self.where_clause_oneline(&item.generics.where_clause); + self.word("= "); + self.neverbreak(); + self.ibox(-INDENT); + self.ty(definition); + self.end(); + self.where_clause_oneline_semi(&item.where_clause_after_eq); + } else { + self.where_clause_oneline_semi(&item.generics.where_clause); + } + self.end(); + self.hardbreak(); + } } } @@ -179,8 +179,8 @@ //! //! ``` //! // [dependencies] -//! // prettyplease = "0.1" -//! // syn = { version = "1", default-features = false, features = ["full", "parsing"] } +//! // prettyplease = "0.2" +//! // syn = { version = "2", default-features = false, features = ["full", "parsing"] } //! //! const INPUT: &str = stringify! { //! use crate::{ @@ -320,7 +320,7 @@ //! these situations with conditional punctuation tokens whose selection can be //! deferred and populated after it's known that the group is or is not broken. -#![doc(html_root_url = "https://docs.rs/prettyplease/0.1.25")] +#![doc(html_root_url = "https://docs.rs/prettyplease/0.2.6")] #![allow( clippy::cast_possible_wrap, clippy::cast_sign_loss, @@ -13,6 +13,8 @@ impl Printer { Lit::Float(lit) => self.lit_float(lit), Lit::Bool(lit) => self.lit_bool(lit), Lit::Verbatim(lit) => self.lit_verbatim(lit), + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown Lit"), } } @@ -1,22 +1,19 @@ use crate::algorithm::Printer; +use crate::path::PathKind; use crate::token::Token; use crate::INDENT; use proc_macro2::{Delimiter, Spacing, TokenStream}; -use syn::{Ident, Macro, MacroDelimiter, PathArguments}; +use syn::{Ident, Macro, MacroDelimiter}; impl Printer { - pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>) { - let is_macro_rules = mac.path.leading_colon.is_none() - && mac.path.segments.len() == 1 - && matches!(mac.path.segments[0].arguments, PathArguments::None) - && mac.path.segments[0].ident == "macro_rules"; - if is_macro_rules { + pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) { + if mac.path.is_ident("macro_rules") { if let Some(ident) = ident { self.macro_rules(ident, &mac.tokens); return; } } - self.path(&mac.path); + self.path(&mac.path, PathKind::Simple); self.word("!"); if let Some(ident) = ident { self.nbsp(); @@ -28,21 +25,22 @@ impl Printer { MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)), }; self.word(open); - self.cbox(INDENT); - delimiter_break(self); - self.ibox(0); - self.macro_rules_tokens(mac.tokens.clone(), false); - self.end(); - delimiter_break(self); - self.offset(-INDENT); - self.end(); + if !mac.tokens.is_empty() { + self.cbox(INDENT); + delimiter_break(self); + self.ibox(0); + self.macro_rules_tokens(mac.tokens.clone(), false); + self.end(); + delimiter_break(self); + self.offset(-INDENT); + self.end(); + } self.word(close); - } - - pub fn mac_semi_if_needed(&mut self, delimiter: &MacroDelimiter) { - match delimiter { - MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"), - MacroDelimiter::Brace(_) => {} + if semicolon { + match mac.delimiter { + MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"), + MacroDelimiter::Brace(_) => {} + } } } @@ -126,7 +124,7 @@ impl Printer { self.word("}"); } - fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) { + pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) { #[derive(PartialEq)] enum State { Start, @@ -211,10 +209,10 @@ impl Printer { fn is_keyword(ident: &Ident) -> bool { match ident.to_string().as_str() { - "as" | "box" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern" - | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" - | "move" | "mut" | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "type" - | "unsafe" | "use" | "where" | "while" | "yield" => true, + "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn" + | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" + | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static" + | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true, _ => false, } } @@ -1,22 +1,24 @@ use crate::algorithm::Printer; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ - FieldPat, Pat, PatBox, PatIdent, PatLit, PatMacro, PatOr, PatPath, PatRange, PatReference, - PatRest, PatSlice, PatStruct, PatTuple, PatTupleStruct, PatType, PatWild, RangeLimits, + FieldPat, Pat, PatIdent, PatOr, PatParen, PatReference, PatRest, PatSlice, PatStruct, PatTuple, + PatTupleStruct, PatType, PatWild, }; impl Printer { pub fn pat(&mut self, pat: &Pat) { match pat { - Pat::Box(pat) => self.pat_box(pat), + Pat::Const(pat) => self.expr_const(pat), Pat::Ident(pat) => self.pat_ident(pat), - Pat::Lit(pat) => self.pat_lit(pat), - Pat::Macro(pat) => self.pat_macro(pat), + Pat::Lit(pat) => self.expr_lit(pat), + Pat::Macro(pat) => self.expr_macro(pat), Pat::Or(pat) => self.pat_or(pat), - Pat::Path(pat) => self.pat_path(pat), - Pat::Range(pat) => self.pat_range(pat), + Pat::Paren(pat) => self.pat_paren(pat), + Pat::Path(pat) => self.expr_path(pat), + Pat::Range(pat) => self.expr_range(pat), Pat::Reference(pat) => self.pat_reference(pat), Pat::Rest(pat) => self.pat_rest(pat), Pat::Slice(pat) => self.pat_slice(pat), @@ -31,12 +33,6 @@ impl Printer { } } - fn pat_box(&mut self, pat: &PatBox) { - self.outer_attrs(&pat.attrs); - self.word("box "); - self.pat(&pat.pat); - } - fn pat_ident(&mut self, pat: &PatIdent) { self.outer_attrs(&pat.attrs); if pat.by_ref.is_some() { @@ -52,16 +48,6 @@ impl Printer { } } - fn pat_lit(&mut self, pat: &PatLit) { - self.outer_attrs(&pat.attrs); - self.expr(&pat.expr); - } - - fn pat_macro(&mut self, pat: &PatMacro) { - self.outer_attrs(&pat.attrs); - self.mac(&pat.mac, None); - } - fn pat_or(&mut self, pat: &PatOr) { self.outer_attrs(&pat.attrs); let mut consistent_break = false; @@ -89,19 +75,11 @@ impl Printer { self.end(); } - fn pat_path(&mut self, pat: &PatPath) { - self.outer_attrs(&pat.attrs); - self.qpath(&pat.qself, &pat.path); - } - - fn pat_range(&mut self, pat: &PatRange) { + fn pat_paren(&mut self, pat: &PatParen) { self.outer_attrs(&pat.attrs); - self.expr(&pat.lo); - match &pat.limits { - RangeLimits::HalfOpen(_) => self.word(".."), - RangeLimits::Closed(_) => self.word("..="), - } - self.expr(&pat.hi); + self.word("("); + self.pat(&pat.pat); + self.word(")"); } fn pat_reference(&mut self, pat: &PatReference) { @@ -131,15 +109,15 @@ impl Printer { fn pat_struct(&mut self, pat: &PatStruct) { self.outer_attrs(&pat.attrs); self.cbox(INDENT); - self.path(&pat.path); + self.path(&pat.path, PathKind::Expr); self.word(" {"); self.space_if_nonempty(); for field in pat.fields.iter().delimited() { self.field_pat(&field); - self.trailing_comma_or_space(field.is_last && pat.dot2_token.is_none()); + self.trailing_comma_or_space(field.is_last && pat.rest.is_none()); } - if pat.dot2_token.is_some() { - self.word(".."); + if let Some(rest) = &pat.rest { + self.pat_rest(rest); self.space(); } self.offset(-INDENT); @@ -170,11 +148,11 @@ impl Printer { fn pat_tuple_struct(&mut self, pat: &PatTupleStruct) { self.outer_attrs(&pat.attrs); - self.path(&pat.path); + self.path(&pat.path, PathKind::Expr); self.word("("); self.cbox(INDENT); self.zerobreak(); - for elem in pat.pat.elems.iter().delimited() { + for elem in pat.elems.iter().delimited() { self.pat(&elem); self.trailing_comma(elem.is_last); } @@ -190,10 +168,68 @@ impl Printer { self.ty(&pat.ty); } + #[cfg(not(feature = "verbatim"))] fn pat_verbatim(&mut self, pat: &TokenStream) { unimplemented!("Pat::Verbatim `{}`", pat); } + #[cfg(feature = "verbatim")] + fn pat_verbatim(&mut self, tokens: &TokenStream) { + use syn::parse::{Parse, ParseStream, Result}; + use syn::{braced, Attribute, Block, Token}; + + enum PatVerbatim { + Box(Pat), + Const(PatConst), + } + + struct PatConst { + attrs: Vec<Attribute>, + block: Block, + } + + impl Parse for PatVerbatim { + fn parse(input: ParseStream) -> Result<Self> { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![box]) { + input.parse::<Token![box]>()?; + let inner = Pat::parse_single(input)?; + Ok(PatVerbatim::Box(inner)) + } else if lookahead.peek(Token![const]) { + input.parse::<Token![const]>()?; + let content; + let brace_token = braced!(content in input); + let attrs = content.call(Attribute::parse_inner)?; + let stmts = content.call(Block::parse_within)?; + Ok(PatVerbatim::Const(PatConst { + attrs, + block: Block { brace_token, stmts }, + })) + } else { + Err(lookahead.error()) + } + } + } + + let pat: PatVerbatim = match syn::parse2(tokens.clone()) { + Ok(pat) => pat, + Err(_) => unimplemented!("Pat::Verbatim `{}`", tokens), + }; + + match pat { + PatVerbatim::Box(pat) => { + self.word("box "); + self.pat(&pat); + } + PatVerbatim::Const(pat) => { + self.word("const "); + self.cbox(INDENT); + self.small_block(&pat.block, &pat.attrs); + self.end(); + } + } + } + fn pat_wild(&mut self, pat: &PatWild) { self.outer_attrs(&pat.attrs); self.word("_"); diff --git a/src/path.rs b/src/path.rs index 53d4b4c..d704bfd 100644 --- a/src/path.rs +++ b/src/path.rs @@ -3,31 +3,41 @@ use crate::iter::IterDelimited; use crate::INDENT; use std::ptr; use syn::{ - AngleBracketedGenericArguments, Binding, Constraint, Expr, GenericArgument, + AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, Expr, GenericArgument, ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf, }; +#[derive(Copy, Clone, PartialEq)] +pub enum PathKind { + // a::B + Simple, + // a::B<T> + Type, + // a::B::<T> + Expr, +} + impl Printer { - pub fn path(&mut self, path: &Path) { + pub fn path(&mut self, path: &Path, kind: PathKind) { assert!(!path.segments.is_empty()); for segment in path.segments.iter().delimited() { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } - self.path_segment(&segment); + self.path_segment(&segment, kind); } } - pub fn path_segment(&mut self, segment: &PathSegment) { + pub fn path_segment(&mut self, segment: &PathSegment, kind: PathKind) { self.ident(&segment.ident); - self.path_arguments(&segment.arguments); + self.path_arguments(&segment.arguments, kind); } - fn path_arguments(&mut self, arguments: &PathArguments) { + fn path_arguments(&mut self, arguments: &PathArguments, kind: PathKind) { match arguments { PathArguments::None => {} PathArguments::AngleBracketed(arguments) => { - self.angle_bracketed_generic_arguments(arguments); + self.angle_bracketed_generic_arguments(arguments, kind); } PathArguments::Parenthesized(arguments) => { self.parenthesized_generic_arguments(arguments); @@ -39,8 +49,6 @@ impl Printer { match arg { GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime), GenericArgument::Type(ty) => self.ty(ty), - GenericArgument::Binding(binding) => self.binding(binding), - GenericArgument::Constraint(constraint) => self.constraint(constraint), GenericArgument::Const(expr) => { match expr { Expr::Lit(expr) => self.expr_lit(expr), @@ -54,41 +62,51 @@ impl Printer { } } } + GenericArgument::AssocType(assoc) => self.assoc_type(assoc), + GenericArgument::AssocConst(assoc) => self.assoc_const(assoc), + GenericArgument::Constraint(constraint) => self.constraint(constraint), + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown GenericArgument"), } } - fn angle_bracketed_generic_arguments(&mut self, generic: &AngleBracketedGenericArguments) { - if generic.args.is_empty() { + pub fn angle_bracketed_generic_arguments( + &mut self, + generic: &AngleBracketedGenericArguments, + path_kind: PathKind, + ) { + if generic.args.is_empty() || path_kind == PathKind::Simple { return; } - if generic.colon2_token.is_some() { + if path_kind == PathKind::Expr { self.word("::"); } self.word("<"); self.cbox(INDENT); self.zerobreak(); - // Print lifetimes before types and consts, all before bindings, - // regardless of their order in self.args. - // - // TODO: ordering rules for const arguments vs type arguments have - // not been settled yet. https://github.com/rust-lang/rust/issues/44580 + // Print lifetimes before types/consts/bindings, regardless of their + // order in self.args. #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, Second, - Third, } fn group(arg: &GenericArgument) -> Group { match arg { GenericArgument::Lifetime(_) => Group::First, - GenericArgument::Type(_) | GenericArgument::Const(_) => Group::Second, - GenericArgument::Binding(_) | GenericArgument::Constraint(_) => Group::Third, + GenericArgument::Type(_) + | GenericArgument::Const(_) + | GenericArgument::AssocType(_) + | GenericArgument::AssocConst(_) + | GenericArgument::Constraint(_) => Group::Second, + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => Group::Second, } } let last = generic.args.iter().max_by_key(|param| group(param)); - for current_group in [Group::First, Group::Second, Group::Third] { + for current_group in [Group::First, Group::Second] { for arg in &generic.args { if group(arg) == current_group { self.generic_argument(arg); @@ -102,14 +120,29 @@ impl Printer { self.word(">"); } - fn binding(&mut self, binding: &Binding) { - self.ident(&binding.ident); + fn assoc_type(&mut self, assoc: &AssocType) { + self.ident(&assoc.ident); + if let Some(generics) = &assoc.generics { + self.angle_bracketed_generic_arguments(generics, PathKind::Type); + } + self.word(" = "); + self.ty(&assoc.ty); + } + + fn assoc_const(&mut self, assoc: &AssocConst) { + self.ident(&assoc.ident); + if let Some(generics) = &assoc.generics { + self.angle_bracketed_generic_arguments(generics, PathKind::Type); + } self.word(" = "); - self.ty(&binding.ty); + self.expr(&assoc.value); } fn constraint(&mut self, constraint: &Constraint) { self.ident(&constraint.ident); + if let Some(generics) = &constraint.generics { + self.angle_bracketed_generic_arguments(generics, PathKind::Type); + } self.ibox(INDENT); for bound in constraint.bounds.iter().delimited() { if bound.is_first { @@ -137,11 +170,11 @@ impl Printer { self.end(); } - pub fn qpath(&mut self, qself: &Option<QSelf>, path: &Path) { + pub fn qpath(&mut self, qself: &Option<QSelf>, path: &Path, kind: PathKind) { let qself = match qself { Some(qself) => qself, None => { - self.path(path); + self.path(path, kind); return; } }; @@ -158,7 +191,7 @@ impl Printer { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } - self.path_segment(&segment); + self.path_segment(&segment, PathKind::Type); if segment.is_last { self.word(">"); } @@ -168,7 +201,7 @@ impl Printer { } for segment in segments { self.word("::"); - self.path_segment(segment); + self.path_segment(segment, kind); } } } diff --git a/src/stmt.rs b/src/stmt.rs index a127b57..268a79e 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -1,5 +1,6 @@ use crate::algorithm::Printer; -use syn::{Expr, Stmt}; +use crate::INDENT; +use syn::{BinOp, Expr, Stmt}; impl Printer { pub fn stmt(&mut self, stmt: &Stmt) { @@ -9,17 +10,32 @@ impl Printer { self.ibox(0); self.word("let "); self.pat(&local.pat); - if let Some((_eq, init)) = &local.init { + if let Some(local_init) = &local.init { self.word(" = "); self.neverbreak(); - self.expr(init); + self.expr(&local_init.expr); + if let Some((_else, diverge)) = &local_init.diverge { + self.word(" else "); + if let Expr::Block(expr) = diverge.as_ref() { + self.small_block(&expr.block, &[]); + } else { + self.word("{"); + self.space(); + self.ibox(INDENT); + self.expr(diverge); + self.end(); + self.space(); + self.offset(-INDENT); + self.word("}"); + } + } } self.word(";"); self.end(); self.hardbreak(); } Stmt::Item(item) => self.item(item), - Stmt::Expr(expr) => { + Stmt::Expr(expr, None) => { if break_after(expr) { self.ibox(0); self.expr_beginning_of_line(expr, true); @@ -32,7 +48,7 @@ impl Printer { self.expr_beginning_of_line(expr, true); } } - Stmt::Semi(expr, _semi) => { + Stmt::Expr(expr, Some(_semi)) => { if let Expr::Verbatim(tokens) = expr { if tokens.is_empty() { return; @@ -46,19 +62,89 @@ impl Printer { self.end(); self.hardbreak(); } + Stmt::Macro(stmt) => { + self.outer_attrs(&stmt.attrs); + let semicolon = true; + self.mac(&stmt.mac, None, semicolon); + self.hardbreak(); + } } } } pub fn add_semi(expr: &Expr) -> bool { match expr { - Expr::Assign(_) - | Expr::AssignOp(_) - | Expr::Break(_) - | Expr::Continue(_) - | Expr::Return(_) - | Expr::Yield(_) => true, + Expr::Assign(_) | Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) | Expr::Yield(_) => { + true + } + Expr::Binary(expr) => match expr.op { + BinOp::AddAssign(_) + | BinOp::SubAssign(_) + | BinOp::MulAssign(_) + | BinOp::DivAssign(_) + | BinOp::RemAssign(_) + | BinOp::BitXorAssign(_) + | BinOp::BitAndAssign(_) + | BinOp::BitOrAssign(_) + | BinOp::ShlAssign(_) + | BinOp::ShrAssign(_) => true, + BinOp::Add(_) + | BinOp::Sub(_) + | BinOp::Mul(_) + | BinOp::Div(_) + | BinOp::Rem(_) + | BinOp::And(_) + | BinOp::Or(_) + | BinOp::BitXor(_) + | BinOp::BitAnd(_) + | BinOp::BitOr(_) + | BinOp::Shl(_) + | BinOp::Shr(_) + | BinOp::Eq(_) + | BinOp::Lt(_) + | BinOp::Le(_) + | BinOp::Ne(_) + | BinOp::Ge(_) + | BinOp::Gt(_) => false, + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + _ => unimplemented!("unknown BinOp"), + }, Expr::Group(group) => add_semi(&group.expr), + + Expr::Array(_) + | Expr::Async(_) + | Expr::Await(_) + | Expr::Block(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Closure(_) + | Expr::Const(_) + | Expr::Field(_) + | Expr::ForLoop(_) + | Expr::If(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Struct(_) + | Expr::Try(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::While(_) => false, + + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => false, } } @@ -80,6 +166,44 @@ fn remove_semi(expr: &Expr) -> bool { Some((_else_token, else_branch)) => remove_semi(else_branch), None => true, }, + + Expr::Array(_) + | Expr::Assign(_) + | Expr::Async(_) + | Expr::Await(_) + | Expr::Binary(_) + | Expr::Block(_) + | Expr::Break(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Closure(_) + | Expr::Continue(_) + | Expr::Const(_) + | Expr::Field(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Struct(_) + | Expr::Try(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::Yield(_) => false, + + #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => false, } } @@ -1,11 +1,12 @@ use crate::algorithm::Printer; use crate::iter::IterDelimited; +use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ - Abi, BareFnArg, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, TypeInfer, - TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject, - TypeTuple, Variadic, + Abi, BareFnArg, BareVariadic, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup, + TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, TypeReference, + TypeSlice, TypeTraitObject, TypeTuple, }; impl Printer { @@ -57,7 +58,7 @@ impl Printer { self.trailing_comma(bare_fn_arg.is_last && ty.variadic.is_none()); } if let Some(variadic) = &ty.variadic { - self.variadic(variadic); + self.bare_variadic(variadic); self.zerobreak(); } self.offset(-INDENT); @@ -86,7 +87,8 @@ impl Printer { } fn type_macro(&mut self, ty: &TypeMacro) { - self.mac(&ty.mac, None); + let semicolon = false; + self.mac(&ty.mac, None, semicolon); } fn type_never(&mut self, ty: &TypeNever) { @@ -101,7 +103,7 @@ impl Printer { } fn type_path(&mut self, ty: &TypePath) { - self.qpath(&ty.qself, &ty.path); + self.qpath(&ty.qself, &ty.path, PathKind::Type); } fn type_ptr(&mut self, ty: &TypePtr) { @@ -162,30 +164,56 @@ impl Printer { #[cfg(not(feature = "verbatim"))] fn type_verbatim(&mut self, ty: &TokenStream) { - if ty.to_string() == "..." { - self.word("..."); - } else { - unimplemented!("Type::Verbatim `{}`", ty); - } + unimplemented!("Type::Verbatim `{}`", ty); } #[cfg(feature = "verbatim")] fn type_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; - use syn::{token, ExprBlock, Lit}; + use syn::punctuated::Punctuated; + use syn::{Token, TypeParamBound}; enum TypeVerbatim { - Lit(Lit), - Block(ExprBlock), + DynStar(DynStar), + MutSelf(MutSelf), + NotType(NotType), + } + + struct DynStar { + bounds: Punctuated<TypeParamBound, Token![+]>, + } + + struct MutSelf { + ty: Option<Type>, + } + + struct NotType { + inner: Type, } impl Parse for TypeVerbatim { fn parse(input: ParseStream) -> Result<Self> { let lookahead = input.lookahead1(); - if lookahead.peek(Lit) { - input.parse().map(TypeVerbatim::Lit) - } else if lookahead.peek(token::Brace) { - input.parse().map(TypeVerbatim::Block) + if lookahead.peek(Token![dyn]) { + input.parse::<Token![dyn]>()?; + input.parse::<Token![*]>()?; + let bounds = input.parse_terminated(TypeParamBound::parse, Token![+])?; + Ok(TypeVerbatim::DynStar(DynStar { bounds })) + } else if lookahead.peek(Token![mut]) { + input.parse::<Token![mut]>()?; + input.parse::<Token![self]>()?; + let ty = if input.is_empty() { + None + } else { + input.parse::<Token![:]>()?; + let ty: Type = input.parse()?; + Some(ty) + }; + Ok(TypeVerbatim::MutSelf(MutSelf { ty })) + } else if lookahead.peek(Token![!]) { + input.parse::<Token![!]>()?; + let inner: Type = input.parse()?; + Ok(TypeVerbatim::NotType(NotType { inner })) } else { Err(lookahead.error()) } @@ -198,11 +226,25 @@ impl Printer { }; match ty { - TypeVerbatim::Lit(lit) => { - self.lit(&lit); + TypeVerbatim::DynStar(ty) => { + self.word("dyn* "); + for type_param_bound in ty.bounds.iter().delimited() { + if !type_param_bound.is_first { + self.word(" + "); + } + self.type_param_bound(&type_param_bound); + } + } + TypeVerbatim::MutSelf(bare_fn_arg) => { + self.word("mut self"); + if let Some(ty) = &bare_fn_arg.ty { + self.word(": "); + self.ty(ty); + } } - TypeVerbatim::Block(block) => { - self.expr_block(&block); + TypeVerbatim::NotType(ty) => { + self.word("!"); + self.ty(&ty.inner); } } } @@ -226,8 +268,12 @@ impl Printer { self.ty(&bare_fn_arg.ty); } - fn variadic(&mut self, variadic: &Variadic) { + fn bare_variadic(&mut self, variadic: &BareVariadic) { self.outer_attrs(&variadic.attrs); + if let Some((name, _colon)) = &variadic.name { + self.ident(name); + self.word(": "); + } self.word("..."); } |