Update prettyplease to 0.2.29
Test: m
Change-Id: I24ddf188c4fc98cf832d1137d581c838a3da154f
diff --git a/crates/prettyplease/.android-checksum.json b/crates/prettyplease/.android-checksum.json
index 00205b2..0df1f50 100644
--- a/crates/prettyplease/.android-checksum.json
+++ b/crates/prettyplease/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"7ae9e7a781bba8e321bc4192826c5653545bb4b05a74de388147bcbde6e2163c","Android.bp":"af0d7dff9013da50fad4d4ae2a6a72b3edcf9e1fb1e642ca3d25f7f27413e2cf","Cargo.toml":"6f04fc844b204a1332d7af8c9fe5c82dae95bfa01698382a272a609933f468bc","LICENSE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-APACHE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"ab3385c35023c2dee8d11dd63dd9a20099b563c2b891c541f33ffd17fb298444","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"6cd24eece6714dc53f7f9f979abdc9cae292e172340688029b4a6ef5ae91ae24","build.rs":"53df216257bb8fbdc2212e23f60cf6261007366a73e9dc390dde3dcbde3d6251","cargo_embargo.json":"0a3aea02264006cd539344c6021258d9fc7c2f88bbe7510b351f5e3485244e08","examples/input.rs":"c54e04743b9eb5665e7a2c0ea6cdd7d3a31fecec1258086b9268b8edf63c9bf7","examples/output.prettyplease.rs":"11211b3ec0b461c64494cec9f240eac5c60084d55a2f9048d41dcca66ebb6f02","examples/output.rustc.rs":"fe3e54f99bba0ad1aebace5e6a06cdf79548dac62524210064f1654a72968f96","examples/output.rustfmt.rs":"f5bcc4d1f873d76ce87834f9fa0d2e5c46042a6f0a2258b22ea4855a85e8b018","src/algorithm.rs":"0631caf6ac9fb58caf49dd2a321a096b21e6b7b4ee3b1e148a4b0e6529d59220","src/attr.rs":"fecce86077baf55f95f0c595579fb5ab1001aa27235a8ad48eaafe1a2aefd8a5","src/convenience.rs":"faae49b9678e3fa882f287594cac24d2544308dbd21a5762b396a174ceb975cd","src/data.rs":"dcb2d7eaa744b5d9190fc90d7cca25d64bff38dc57f746581d6097b79ca67d03","src/expr.rs":"ef67c23508873d9f33004fcb6b63d2163d8a0873a118d98576fea193dc6a5e05","src/file.rs":"359101ac28e7e39a275b9837053e6841765a48d022ea1482d8ef80e34d860d44","src/generics.rs":"73491a105c69318d01a6d090778d57102937f89fd9cd4cb8f2857c12a79e8dd5","src/item.rs":"c8f065dba1547b57c369e5b11956420d9e0979a6d5536225b370901a8160b7cc","src/iter.rs":"63d7cf3313d8ed3fa1637c5c01d80ab48b8a742538b71fd792604246822d4d9d","src/lib.rs":"bcce5af8455aa032e41a165cbe064b719b9289af13627057c89a6526066fd1cf","src/lifetime.rs":"b2f01d35e9faddd4a042dec30bb0f1668683ef11a9477e660e9eb4d480a08d61","src/lit.rs":"39cd69038e2143b16dad6007033015ef4c8e116b4af78e12937e916aa92b8376","src/mac.rs":"3df8a64cdb74ad433afe6a99f4d305bafbed0e7b2773f60fd7afaba91890a208","src/pat.rs":"d3ff013f425aa46d26d8c1c075c4bdd9e924f198125d5793d409941c31bf1ae9","src/path.rs":"c2c9a6f5fe868446427bb76ee17bbe14e6fb1fcd0d3d325fd3115f4057586638","src/ring.rs":"d2f765e033eea3aad1b8fd4351e8a3d32ff0c79be13a0104683d5fd38d0e131b","src/stmt.rs":"bce93e2b3af162871814b77ef1c350a4d0f71e423f74d1e5e93276dc39f1e23d","src/token.rs":"881884adcb68dcd01f6d66459c7f49df2f6f8019f0718d0a146d9c23ea375af5","src/ty.rs":"526800996453fd7068fc8f3e7e22a873f20bbcd89cd29f63e0cd32692b7559c1","tests/test.rs":"42b5f9d824aff128e6caf5df9602fe36b279b32d5debcaecf68969fbff09af4f"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"8754f5c95b4603c91b2fee04c7b22563bd3dac2e437fd8f33f861b95deda886d","Android.bp":"daa37880cfc6788215c9ac3b857bba421107f5dde60828f412396b57b64bddc5","Cargo.toml":"5ea38f196bfa1d0123a5f3129ff7cd7c2da6f9d1987c015d370236a0522722c3","LICENSE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-APACHE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"c6c17d8cdb6c025f663b86e078bd9350f16593db38063b9ff5708c981b48d2fd","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"6cd24eece6714dc53f7f9f979abdc9cae292e172340688029b4a6ef5ae91ae24","build.rs":"53df216257bb8fbdc2212e23f60cf6261007366a73e9dc390dde3dcbde3d6251","cargo_embargo.json":"0a3aea02264006cd539344c6021258d9fc7c2f88bbe7510b351f5e3485244e08","examples/input.rs":"c54e04743b9eb5665e7a2c0ea6cdd7d3a31fecec1258086b9268b8edf63c9bf7","examples/output.prettyplease.rs":"11211b3ec0b461c64494cec9f240eac5c60084d55a2f9048d41dcca66ebb6f02","examples/output.rustc.rs":"fe3e54f99bba0ad1aebace5e6a06cdf79548dac62524210064f1654a72968f96","examples/output.rustfmt.rs":"f5bcc4d1f873d76ce87834f9fa0d2e5c46042a6f0a2258b22ea4855a85e8b018","src/algorithm.rs":"b7ae58dedbca564ea449f02160084aa9a4f38a7b724afbdb19e81f7e524867ce","src/attr.rs":"dd3e5c4c2c4a65040f3b567c86405e823e8eb1f963aaa8b8a8547fc1de22ca31","src/classify.rs":"e17fd6644f59523bd6f7a198acf3645fd1a0930f5cbfebe5552918ea8841e38e","src/convenience.rs":"faae49b9678e3fa882f287594cac24d2544308dbd21a5762b396a174ceb975cd","src/data.rs":"25457e0091113479a29087a215dc22f32eb28c9f4e29fb43f8864f81d6b98bf3","src/expr.rs":"fe7d9645bc3f86c63a58bc72aeb69b4ad11bc0651cc37b4e4f6e3b5a20708275","src/file.rs":"359101ac28e7e39a275b9837053e6841765a48d022ea1482d8ef80e34d860d44","src/fixup.rs":"2f258a9d48fc6851ec32dc7bfc492f1187c2438fe3504b3a73ea0dfa91087d32","src/generics.rs":"eab714ff712509d0e2b23a635eb6ebabeb711d82b8e77350ace505180e5f60ef","src/item.rs":"21d89d8eb6376057bc3ffcfe7e0f18cb72803e1656fac5ec6919a93a85ef2939","src/iter.rs":"63d7cf3313d8ed3fa1637c5c01d80ab48b8a742538b71fd792604246822d4d9d","src/lib.rs":"c4700c7472d0a9eeb43c25f9f42187e75d203dd0d07416649c5d588ef64f721c","src/lifetime.rs":"b2f01d35e9faddd4a042dec30bb0f1668683ef11a9477e660e9eb4d480a08d61","src/lit.rs":"39cd69038e2143b16dad6007033015ef4c8e116b4af78e12937e916aa92b8376","src/mac.rs":"131791d63273f8ae09cbb301635f9e2cbee3f8b5dd89a55c376590b15b7de911","src/pat.rs":"40e2926be48bcdfd151e6de00880ad4aba6c7822db3eb340e3f12a6a50edae4b","src/path.rs":"68c7066f63d9eb570d0d5a613225f2948d0d547257ecd49ea29e7cf693587d24","src/precedence.rs":"939141754675dbb9bc6091123a847d62cf8493db0aa701686f833b0435d3fc40","src/ring.rs":"cd1a93144b2f76a6a47ad8847ddc90d9b3d13bf674243e774f7848d7baebbcd8","src/stmt.rs":"10372120c97c4f3840f7b494bf973c36c97625b68f60f178854726b622b5fc0d","src/token.rs":"881884adcb68dcd01f6d66459c7f49df2f6f8019f0718d0a146d9c23ea375af5","src/ty.rs":"e8d75df0fe6e83a50a288b6b0ffe2285d698810043e6ba6a59408b62ecb04383","tests/test.rs":"adf75b6cb0fe9c700ea366366e2b0b9a2e166983b4dfd5abe87c1bcc986cbdea","tests/test_precedence.rs":"64413a1314a13c027b8e5e6afbfcff4d3375f5db80131fffc7b341d89fb775a6"}}
\ No newline at end of file
diff --git a/crates/prettyplease/.cargo-checksum.json b/crates/prettyplease/.cargo-checksum.json
index 434d068..823e7b4 100644
--- a/crates/prettyplease/.cargo-checksum.json
+++ b/crates/prettyplease/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"0e165ac6d5ca01781b9c1586f976e6abeedaa534f1ba0dc0adf607f9b30795e0","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a7e6d152cdc6ea603077e50b8d55af374d9d21fd9f62d08a008588b17d785e6e","build.rs":"79a5b2d260aa97aeac7105fbfa00774982f825cd708c100ea96d01c39974bb88","examples/input.rs":"53350088f12a346a99034af41ef432dedcc9e5d581c5592d9aae3807c42656c1","examples/output.prettyplease.rs":"fa63c118daadb64c456ec5b8d5e46e5d7fabbbeb6a6e61a08eabc23360a18fbd","examples/output.rustc.rs":"0c66f8929fa40a2822d4ea1aec3d8b83db598aba043b17f3c1a6133f0d657135","examples/output.rustfmt.rs":"914a9aea1c51e097bfd80c9af4011811e6126c9df5fb0eac3d40b1203fba7c58","src/algorithm.rs":"7a4a7b62f5300aabf7fcff3cd8c8aaa3ae1e3d1dc717eea1e4797988ce0e18c7","src/attr.rs":"54e829ae468f22c8e2853d669515575f1444bfee026cfd9b19538f77caf10ab7","src/convenience.rs":"dd392b009b691d3587c7d8e3caeaacf450303c4223792b5f89c336358e371c39","src/data.rs":"9db6623d3ccc79b541a28bdc88875ad0036576689e085007eb362819f8e9a2d3","src/expr.rs":"45a30de0afdf8e0564c221ebcb4cf11e2d8948b5b85cc99d5d55d1293e65dea9","src/file.rs":"5689efa3c5959a6a0d8cfc2c13bf8a37ab0669e2b81dbded3f3c28884a88fca0","src/generics.rs":"14a01f0602c005939f0324ef5e6b6624f477a531552bc764447e08875fb2ebbb","src/item.rs":"6793b19d8be3dd7cb75a984370a771d0592b9c4548d294cf246424947a3c8535","src/iter.rs":"38b2cd3b38719c6024fb6b3aa739f6f8736c83193fd21e2365d4f6c27bc41666","src/lib.rs":"a6c8e3e455f6b2f5b0800213464076f8ac49fc64aeddd77f3e4f0f9dbf554b18","src/lifetime.rs":"6d420430168185b2da3409bc38a45f63cced9443915f04e6aec71367fc070dcf","src/lit.rs":"9ea6d25533e64df4ff01c084fa1c31ddf64fb3b159409eec7d80dbf281e5171e","src/mac.rs":"62911747c308187afc750b4bd4f8bd24ee825081043d78da68a001ea55ab5853","src/pat.rs":"d130b141a3fd8098913cb179efe6600a5f010a2719447ecb3d4fb29e9b546220","src/path.rs":"4d3f7caa0bfe821bdb9ad00ee1d7d61101aef8a5cbea35d565b6da6217feefca","src/ring.rs":"e23d133209b977e457b07b0cd93b3711d01f4172d7cfa4cf6a7247637390e606","src/stmt.rs":"763f617a5535f8e61593b0cb1c6c9f5caef032085671dbce509b691d94d39835","src/token.rs":"c288b1d81f2a35673d4ca1dd10d3386670b067460121df3038303e1ed73b41a7","src/ty.rs":"b5fa5f318c5e4593ed3eed4707e63dceaa62eea4ecdfba0f59bd946917ca3dbd","tests/test.rs":"04994ad0a37c43457390f11ade6ab74fc26c5e879b0a374f0b64acb530a0a496"},"package":"64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"}
\ No newline at end of file
+{"files":{"Cargo.toml":"06e68c5a91a65141eef6ddfbc87652ffa90a3ab71175b31021d61e6eb8e7b8e1","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a7e6d152cdc6ea603077e50b8d55af374d9d21fd9f62d08a008588b17d785e6e","build.rs":"79a5b2d260aa97aeac7105fbfa00774982f825cd708c100ea96d01c39974bb88","examples/input.rs":"53350088f12a346a99034af41ef432dedcc9e5d581c5592d9aae3807c42656c1","examples/output.prettyplease.rs":"fa63c118daadb64c456ec5b8d5e46e5d7fabbbeb6a6e61a08eabc23360a18fbd","examples/output.rustc.rs":"0c66f8929fa40a2822d4ea1aec3d8b83db598aba043b17f3c1a6133f0d657135","examples/output.rustfmt.rs":"914a9aea1c51e097bfd80c9af4011811e6126c9df5fb0eac3d40b1203fba7c58","src/algorithm.rs":"bae517cd89743dd4bccb7cf7bf659c9e138d68cf4b06030446effb794811a36c","src/attr.rs":"c11f2ed0d16f2a7c1a0023f2fc5c81a074276ff399679b2814ab762edb8dd792","src/classify.rs":"2ce2d63ad9071aac10b1037e6382703736e0147d96b3ccf32a53182d12883f1b","src/convenience.rs":"dd392b009b691d3587c7d8e3caeaacf450303c4223792b5f89c336358e371c39","src/data.rs":"5bc2dce1cfa1aa5c1324ccdc2d76a6bd5df2382530c7e863d2bb50dea60cc4bc","src/expr.rs":"e99c5ef631cff8a0b0c2666deb35cd0c3eee2ee3585b113ff37cffe3b12435d4","src/file.rs":"5689efa3c5959a6a0d8cfc2c13bf8a37ab0669e2b81dbded3f3c28884a88fca0","src/fixup.rs":"ee279996cd5c57eb308a7e8529cd1f2999617426047df2e641a95ddc82ff44ce","src/generics.rs":"f10b95f4b011f5bf6510d3a77e38227716dccf0a8aeb8a8344e776be9f90f54e","src/item.rs":"4dc320bf73f4ca7d2c6741c31700b5150a02106bfe688969070aff53c2ce455a","src/iter.rs":"38b2cd3b38719c6024fb6b3aa739f6f8736c83193fd21e2365d4f6c27bc41666","src/lib.rs":"f92f24de5bc5231d107bf97c10d845f83e6c0ae922c8d33841f8c34d07c76130","src/lifetime.rs":"6d420430168185b2da3409bc38a45f63cced9443915f04e6aec71367fc070dcf","src/lit.rs":"9ea6d25533e64df4ff01c084fa1c31ddf64fb3b159409eec7d80dbf281e5171e","src/mac.rs":"36c62d1b721c6c56af799c55371ee43e9c446c4787ba1f69021b9cb6acda76b9","src/pat.rs":"8e53fd1b5382bb068210162bfab9921246093cfdd80dd93cd8627fcfdae39940","src/path.rs":"e73d83dc38f5c6c0c82f824da7eb090a16027f32fc40446b185580ee5e99be58","src/precedence.rs":"a8ce97ba0a25f442b5f238c64f078d70f4114b4b0f9df82764d533dd39a47abb","src/ring.rs":"517b1a02f8e0a9c1316830117daad1e30d17e1fcf6428c6b438c626aa43286ae","src/stmt.rs":"e17ab9647fed9daa4f5b2fbd007015128f2a7fc65686a988593444a37242f885","src/token.rs":"c288b1d81f2a35673d4ca1dd10d3386670b067460121df3038303e1ed73b41a7","src/ty.rs":"a1e3e5a08124673826948f97e70c11800081d2bca7f3aec12d84d0d00837290f","tests/test.rs":"c6f8c7830b7491fca1d56e41aa4acc6256b683a3556a48982f57ae62d38aaaa2","tests/test_precedence.rs":"de0c770b9a72e5eba8a52dcac0614d6db8ff5041ba601e1e67f113d68c9afd50"},"package":"6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"}
\ No newline at end of file
diff --git a/crates/prettyplease/Android.bp b/crates/prettyplease/Android.bp
index 8e5fd1a..1e0bada 100644
--- a/crates/prettyplease/Android.bp
+++ b/crates/prettyplease/Android.bp
@@ -18,7 +18,7 @@
host_cross_supported: false,
crate_name: "prettyplease",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.25",
+ cargo_pkg_version: "0.2.29",
crate_root: "src/lib.rs",
edition: "2021",
rustlibs: [
diff --git a/crates/prettyplease/Cargo.toml b/crates/prettyplease/Cargo.toml
index b39c5e3..14dd304 100644
--- a/crates/prettyplease/Cargo.toml
+++ b/crates/prettyplease/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.61"
+rust-version = "1.62"
name = "prettyplease"
-version = "0.2.25"
+version = "0.2.29"
authors = ["David Tolnay <[email protected]>"]
build = "build.rs"
links = "prettyplease02"
@@ -38,6 +38,9 @@
[package.metadata.playground]
features = ["verbatim"]
+[features]
+verbatim = ["syn/parsing"]
+
[lib]
name = "prettyplease"
path = "src/lib.rs"
@@ -47,12 +50,16 @@
name = "test"
path = "tests/test.rs"
+[[test]]
+name = "test_precedence"
+path = "tests/test_precedence.rs"
+
[dependencies.proc-macro2]
version = "1.0.80"
default-features = false
[dependencies.syn]
-version = "2.0.81"
+version = "2.0.96"
features = ["full"]
default-features = false
@@ -68,9 +75,12 @@
default-features = false
[dev-dependencies.syn]
-version = "2.0.81"
-features = ["parsing"]
+version = "2.0.96"
+features = [
+ "clone-impls",
+ "extra-traits",
+ "parsing",
+ "printing",
+ "visit-mut",
+]
default-features = false
-
-[features]
-verbatim = ["syn/parsing"]
diff --git a/crates/prettyplease/METADATA b/crates/prettyplease/METADATA
index 13f0ff2..8344b06 100644
--- a/crates/prettyplease/METADATA
+++ b/crates/prettyplease/METADATA
@@ -1,17 +1,17 @@
name: "prettyplease"
description: "A minimal `syn` syntax tree pretty-printer"
third_party {
- version: "0.2.25"
+ version: "0.2.29"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 12
- day: 20
+ year: 2025
+ month: 1
+ day: 14
}
homepage: "https://crates.io/crates/prettyplease"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/prettyplease/prettyplease-0.2.25.crate"
- version: "0.2.25"
+ value: "https://static.crates.io/crates/prettyplease/prettyplease-0.2.29.crate"
+ version: "0.2.29"
}
}
diff --git a/crates/prettyplease/src/algorithm.rs b/crates/prettyplease/src/algorithm.rs
index 6e2b961..ecb68c3 100644
--- a/crates/prettyplease/src/algorithm.rs
+++ b/crates/prettyplease/src/algorithm.rs
@@ -19,7 +19,7 @@
pub offset: isize,
pub blank_space: usize,
pub pre_break: Option<char>,
- pub post_break: Option<char>,
+ pub post_break: &'static str,
pub no_break: Option<char>,
pub if_nonempty: bool,
pub never_break: bool,
@@ -211,9 +211,18 @@
self.scan_end();
}
+ pub fn ends_with(&self, ch: char) -> bool {
+ for i in self.buf.index_range().rev() {
+ if let Token::String(token) = &self.buf[i].token {
+ return token.ends_with(ch);
+ }
+ }
+ self.out.ends_with(ch)
+ }
+
fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
- if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
+ if *self.scan_stack.front().unwrap() == self.buf.index_range().start {
self.scan_stack.pop_front().unwrap();
self.buf.first_mut().size = SIZE_INFINITY;
}
@@ -353,10 +362,10 @@
let indent = self.indent as isize + token.offset;
self.pending_indentation = usize::try_from(indent).unwrap();
self.space = cmp::max(MARGIN - indent, MIN_SPACE);
- if let Some(post_break) = token.post_break {
+ if !token.post_break.is_empty() {
self.print_indent();
- self.out.push(post_break);
- self.space -= post_break.len_utf8() as isize;
+ self.out.push_str(token.post_break);
+ self.space -= token.post_break.len() as isize;
}
}
}
diff --git a/crates/prettyplease/src/attr.rs b/crates/prettyplease/src/attr.rs
index 0388d66..d7c2250 100644
--- a/crates/prettyplease/src/attr.rs
+++ b/crates/prettyplease/src/attr.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::path::PathKind;
use crate::INDENT;
use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
@@ -102,7 +103,7 @@
fn meta_name_value(&mut self, meta: &MetaNameValue) {
self.path(&meta.path, PathKind::Simple);
self.word(" = ");
- self.expr(&meta.value);
+ self.expr(&meta.value, FixupContext::NONE);
}
fn attr_tokens(&mut self, tokens: TokenStream) {
diff --git a/crates/prettyplease/src/classify.rs b/crates/prettyplease/src/classify.rs
new file mode 100644
index 0000000..17648f6
--- /dev/null
+++ b/crates/prettyplease/src/classify.rs
@@ -0,0 +1,324 @@
+use proc_macro2::{Delimiter, TokenStream, TokenTree};
+use std::ops::ControlFlow;
+use syn::punctuated::Punctuated;
+use syn::{Expr, MacroDelimiter, Path, PathArguments, ReturnType, Token, Type, TypeParamBound};
+
+pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool {
+ match expr {
+ Expr::Macro(expr) => !matches!(expr.mac.delimiter, MacroDelimiter::Brace(_)),
+ _ => requires_comma_to_be_match_arm(expr),
+ }
+}
+
+pub(crate) fn requires_comma_to_be_match_arm(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::If(_)
+ | Expr::Match(_)
+ | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc
+ | Expr::While(_)
+ | Expr::Loop(_)
+ | Expr::ForLoop(_)
+ | Expr::TryBlock(_)
+ | Expr::Const(_) => return false,
+
+ Expr::Array(_)
+ | Expr::Assign(_)
+ | Expr::Async(_)
+ | Expr::Await(_)
+ | Expr::Binary(_)
+ | Expr::Break(_)
+ | Expr::Call(_)
+ | Expr::Cast(_)
+ | Expr::Closure(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Let(_)
+ | Expr::Lit(_)
+ | Expr::Macro(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Range(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::Try(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Yield(_)
+ | Expr::Verbatim(_) => return true,
+
+ Expr::Group(group) => expr = &group.expr,
+
+ _ => return true,
+ }
+ }
+}
+
+pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool {
+ loop {
+ match ty {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Type::BareFn(t) => match &t.output {
+ ReturnType::Default => return false,
+ ReturnType::Type(_, ret) => ty = ret,
+ },
+ Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Path(t) => match last_type_in_path(&t.path) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Ptr(t) => ty = &t.elem,
+ Type::Reference(t) => ty = &t.elem,
+ Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+
+ Type::Array(_)
+ | Type::Group(_)
+ | Type::Infer(_)
+ | Type::Macro(_)
+ | Type::Never(_)
+ | Type::Paren(_)
+ | Type::Slice(_)
+ | Type::Tuple(_)
+ | Type::Verbatim(_) => return false,
+
+ _ => return false,
+ }
+ }
+
+ fn last_type_in_path(path: &Path) -> ControlFlow<bool, &Type> {
+ match &path.segments.last().unwrap().arguments {
+ PathArguments::None => ControlFlow::Break(true),
+ PathArguments::AngleBracketed(_) => ControlFlow::Break(false),
+ PathArguments::Parenthesized(arg) => match &arg.output {
+ ReturnType::Default => ControlFlow::Break(false),
+ ReturnType::Type(_, ret) => ControlFlow::Continue(ret),
+ },
+ }
+ }
+
+ fn last_type_in_bounds(
+ bounds: &Punctuated<TypeParamBound, Token![+]>,
+ ) -> ControlFlow<bool, &Type> {
+ match bounds.last().unwrap() {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ TypeParamBound::Trait(t) => last_type_in_path(&t.path),
+ TypeParamBound::Lifetime(_)
+ | TypeParamBound::PreciseCapture(_)
+ | TypeParamBound::Verbatim(_) => ControlFlow::Break(false),
+ _ => ControlFlow::Break(false),
+ }
+ }
+}
+
+/// Whether the expression's first token is the label of a loop/block.
+pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Block(e) => return e.label.is_some(),
+ Expr::ForLoop(e) => return e.label.is_some(),
+ Expr::Loop(e) => return e.label.is_some(),
+ Expr::While(e) => return e.label.is_some(),
+
+ Expr::Assign(e) => expr = &e.left,
+ Expr::Await(e) => expr = &e.base,
+ Expr::Binary(e) => expr = &e.left,
+ Expr::Call(e) => expr = &e.func,
+ Expr::Cast(e) => expr = &e.expr,
+ Expr::Field(e) => expr = &e.base,
+ Expr::Index(e) => expr = &e.expr,
+ Expr::MethodCall(e) => expr = &e.receiver,
+ Expr::Range(e) => match &e.start {
+ Some(start) => expr = start,
+ None => return false,
+ },
+ Expr::Try(e) => expr = &e.expr,
+
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::If(_)
+ | Expr::Infer(_)
+ | Expr::Let(_)
+ | Expr::Lit(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::Yield(_) => return false,
+
+ Expr::Group(e) => {
+ if !e.attrs.is_empty() {
+ return false;
+ }
+ expr = &e.expr;
+ }
+
+ _ => return false,
+ }
+ }
+}
+
+/// Whether the expression's last token is `}`.
+pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Async(_)
+ | Expr::Block(_)
+ | Expr::Const(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Loop(_)
+ | Expr::Match(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Unsafe(_)
+ | Expr::While(_) => return true,
+
+ Expr::Assign(e) => expr = &e.right,
+ Expr::Binary(e) => expr = &e.right,
+ Expr::Break(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+ Expr::Cast(e) => return type_trailing_brace(&e.ty),
+ Expr::Closure(e) => expr = &e.body,
+ Expr::Group(e) => expr = &e.expr,
+ Expr::Let(e) => expr = &e.expr,
+ Expr::Macro(e) => return matches!(e.mac.delimiter, MacroDelimiter::Brace(_)),
+ Expr::Range(e) => match &e.end {
+ Some(end) => expr = end,
+ None => return false,
+ },
+ Expr::RawAddr(e) => expr = &e.expr,
+ Expr::Reference(e) => expr = &e.expr,
+ Expr::Return(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+ Expr::Unary(e) => expr = &e.expr,
+ Expr::Verbatim(e) => return tokens_trailing_brace(e),
+ Expr::Yield(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+
+ Expr::Array(_)
+ | Expr::Await(_)
+ | Expr::Call(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Repeat(_)
+ | Expr::Try(_)
+ | Expr::Tuple(_) => return false,
+
+ _ => return false,
+ }
+ }
+
+ fn type_trailing_brace(mut ty: &Type) -> bool {
+ loop {
+ match ty {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Type::BareFn(t) => match &t.output {
+ ReturnType::Default => return false,
+ ReturnType::Type(_, ret) => ty = ret,
+ },
+ Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_brace) => return trailing_brace,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Macro(t) => return matches!(t.mac.delimiter, MacroDelimiter::Brace(_)),
+ Type::Path(t) => match last_type_in_path(&t.path) {
+ Some(t) => ty = t,
+ None => return false,
+ },
+ Type::Ptr(t) => ty = &t.elem,
+ Type::Reference(t) => ty = &t.elem,
+ Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_brace) => return trailing_brace,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Verbatim(t) => return tokens_trailing_brace(t),
+
+ Type::Array(_)
+ | Type::Group(_)
+ | Type::Infer(_)
+ | Type::Never(_)
+ | Type::Paren(_)
+ | Type::Slice(_)
+ | Type::Tuple(_) => return false,
+
+ _ => return false,
+ }
+ }
+ }
+
+ fn last_type_in_path(path: &Path) -> Option<&Type> {
+ match &path.segments.last().unwrap().arguments {
+ PathArguments::None | PathArguments::AngleBracketed(_) => None,
+ PathArguments::Parenthesized(arg) => match &arg.output {
+ ReturnType::Default => None,
+ ReturnType::Type(_, ret) => Some(ret),
+ },
+ }
+ }
+
+ fn last_type_in_bounds(
+ bounds: &Punctuated<TypeParamBound, Token![+]>,
+ ) -> ControlFlow<bool, &Type> {
+ match bounds.last().unwrap() {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ TypeParamBound::Trait(t) => match last_type_in_path(&t.path) {
+ Some(t) => ControlFlow::Continue(t),
+ None => ControlFlow::Break(false),
+ },
+ TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => {
+ ControlFlow::Break(false)
+ }
+ TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)),
+ _ => ControlFlow::Break(false),
+ }
+ }
+
+ fn tokens_trailing_brace(tokens: &TokenStream) -> bool {
+ if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() {
+ last.delimiter() == Delimiter::Brace
+ } else {
+ false
+ }
+ }
+}
diff --git a/crates/prettyplease/src/data.rs b/crates/prettyplease/src/data.rs
index d823ee3..3561a49 100644
--- a/crates/prettyplease/src/data.rs
+++ b/crates/prettyplease/src/data.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -31,7 +32,7 @@
}
if let Some((_eq_token, discriminant)) = &variant.discriminant {
self.word(" = ");
- self.expr(discriminant);
+ self.expr(discriminant, FixupContext::NONE);
}
}
diff --git a/crates/prettyplease/src/expr.rs b/crates/prettyplease/src/expr.rs
index 52c9e5a..7b223b8 100644
--- a/crates/prettyplease/src/expr.rs
+++ b/crates/prettyplease/src/expr.rs
@@ -1,7 +1,10 @@
use crate::algorithm::{BreakToken, Printer};
use crate::attr;
+use crate::classify;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
+use crate::precedence::Precedence;
use crate::stmt;
use crate::INDENT;
use proc_macro2::TokenStream;
@@ -17,94 +20,130 @@
};
impl Printer {
- pub fn expr(&mut self, expr: &Expr) {
+ pub fn expr(&mut self, expr: &Expr, mut fixup: FixupContext) {
+ let needs_paren = fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
let beginning_of_line = false;
+
match expr {
#![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
Expr::Array(expr) => self.expr_array(expr),
- Expr::Assign(expr) => self.expr_assign(expr),
+ Expr::Assign(expr) => self.expr_assign(expr, fixup),
Expr::Async(expr) => self.expr_async(expr),
- Expr::Await(expr) => self.expr_await(expr, beginning_of_line),
- Expr::Binary(expr) => self.expr_binary(expr),
+ Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup),
+ Expr::Binary(expr) => self.expr_binary(expr, fixup),
Expr::Block(expr) => self.expr_block(expr),
- Expr::Break(expr) => self.expr_break(expr),
- Expr::Call(expr) => self.expr_call(expr, beginning_of_line),
- Expr::Cast(expr) => self.expr_cast(expr),
- Expr::Closure(expr) => self.expr_closure(expr),
+ Expr::Break(expr) => self.expr_break(expr, fixup),
+ Expr::Call(expr) => self.expr_call(expr, beginning_of_line, fixup),
+ Expr::Cast(expr) => self.expr_cast(expr, fixup),
+ Expr::Closure(expr) => self.expr_closure(expr, fixup),
Expr::Const(expr) => self.expr_const(expr),
Expr::Continue(expr) => self.expr_continue(expr),
- Expr::Field(expr) => self.expr_field(expr, beginning_of_line),
+ Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup),
Expr::ForLoop(expr) => self.expr_for_loop(expr),
- Expr::Group(expr) => self.expr_group(expr),
+ Expr::Group(expr) => self.expr_group(expr, fixup),
Expr::If(expr) => self.expr_if(expr),
- Expr::Index(expr) => self.expr_index(expr, beginning_of_line),
+ Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup),
Expr::Infer(expr) => self.expr_infer(expr),
- Expr::Let(expr) => self.expr_let(expr),
+ Expr::Let(expr) => self.expr_let(expr, fixup),
Expr::Lit(expr) => self.expr_lit(expr),
Expr::Loop(expr) => self.expr_loop(expr),
Expr::Macro(expr) => self.expr_macro(expr),
Expr::Match(expr) => self.expr_match(expr),
- Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line),
+ Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup),
Expr::Paren(expr) => self.expr_paren(expr),
Expr::Path(expr) => self.expr_path(expr),
- Expr::Range(expr) => self.expr_range(expr),
- Expr::RawAddr(expr) => self.expr_raw_addr(expr),
- Expr::Reference(expr) => self.expr_reference(expr),
+ Expr::Range(expr) => self.expr_range(expr, fixup),
+ Expr::RawAddr(expr) => self.expr_raw_addr(expr, fixup),
+ Expr::Reference(expr) => self.expr_reference(expr, fixup),
Expr::Repeat(expr) => self.expr_repeat(expr),
- Expr::Return(expr) => self.expr_return(expr),
+ Expr::Return(expr) => self.expr_return(expr, fixup),
Expr::Struct(expr) => self.expr_struct(expr),
- Expr::Try(expr) => self.expr_try(expr, beginning_of_line),
+ Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup),
Expr::TryBlock(expr) => self.expr_try_block(expr),
Expr::Tuple(expr) => self.expr_tuple(expr),
- Expr::Unary(expr) => self.expr_unary(expr),
+ Expr::Unary(expr) => self.expr_unary(expr, fixup),
Expr::Unsafe(expr) => self.expr_unsafe(expr),
- Expr::Verbatim(expr) => self.expr_verbatim(expr),
+ Expr::Verbatim(expr) => self.expr_verbatim(expr, fixup),
Expr::While(expr) => self.expr_while(expr),
- Expr::Yield(expr) => self.expr_yield(expr),
+ Expr::Yield(expr) => self.expr_yield(expr, fixup),
_ => unimplemented!("unknown Expr"),
}
- }
- pub fn expr_beginning_of_line(&mut self, expr: &Expr, beginning_of_line: bool) {
- match expr {
- Expr::Await(expr) => self.expr_await(expr, beginning_of_line),
- Expr::Field(expr) => self.expr_field(expr, beginning_of_line),
- Expr::Index(expr) => self.expr_index(expr, beginning_of_line),
- Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line),
- Expr::Try(expr) => self.expr_try(expr, beginning_of_line),
- _ => self.expr(expr),
- }
- }
-
- fn subexpr(&mut self, expr: &Expr, beginning_of_line: bool) {
- match expr {
- Expr::Await(expr) => self.subexpr_await(expr, beginning_of_line),
- Expr::Call(expr) => self.subexpr_call(expr),
- Expr::Field(expr) => self.subexpr_field(expr, beginning_of_line),
- Expr::Index(expr) => self.subexpr_index(expr, beginning_of_line),
- Expr::MethodCall(expr) => {
- let unindent_call_args = false;
- self.subexpr_method_call(expr, beginning_of_line, unindent_call_args);
- }
- Expr::Try(expr) => self.subexpr_try(expr, beginning_of_line),
- _ => {
- self.cbox(-INDENT);
- self.expr(expr);
- self.end();
- }
- }
- }
-
- fn wrap_exterior_struct(&mut self, expr: &Expr) {
- let needs_paren = contains_exterior_struct_lit(expr);
- if needs_paren {
- self.word("(");
- }
- self.cbox(0);
- self.expr(expr);
if needs_paren {
self.word(")");
}
+ }
+
+ pub fn expr_beginning_of_line(
+ &mut self,
+ expr: &Expr,
+ mut needs_paren: bool,
+ beginning_of_line: bool,
+ mut fixup: FixupContext,
+ ) {
+ needs_paren |= fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ match expr {
+ Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup),
+ Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup),
+ Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup),
+ Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup),
+ Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup),
+ _ => self.expr(expr, fixup),
+ }
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
+ fn prefix_subexpr(
+ &mut self,
+ expr: &Expr,
+ mut needs_paren: bool,
+ beginning_of_line: bool,
+ mut fixup: FixupContext,
+ ) {
+ needs_paren |= fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ match expr {
+ Expr::Await(expr) => self.prefix_subexpr_await(expr, beginning_of_line, fixup),
+ Expr::Call(expr) => self.prefix_subexpr_call(expr, fixup),
+ Expr::Field(expr) => self.prefix_subexpr_field(expr, beginning_of_line, fixup),
+ Expr::Index(expr) => self.prefix_subexpr_index(expr, beginning_of_line, fixup),
+ Expr::MethodCall(expr) => {
+ let unindent_call_args = false;
+ self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup);
+ }
+ Expr::Try(expr) => self.prefix_subexpr_try(expr, beginning_of_line, fixup),
+ _ => {
+ self.cbox(-INDENT);
+ self.expr(expr, fixup);
+ self.end();
+ }
+ }
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
+ fn expr_condition(&mut self, expr: &Expr) {
+ self.cbox(0);
+ self.expr(expr, FixupContext::new_condition());
if needs_newline_if_wrap(expr) {
self.space();
} else {
@@ -113,13 +152,26 @@
self.end();
}
+ pub fn subexpr(&mut self, expr: &Expr, needs_paren: bool, mut fixup: FixupContext) {
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ self.expr(expr, fixup);
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
fn expr_array(&mut self, expr: &ExprArray) {
self.outer_attrs(&expr.attrs);
self.word("[");
self.cbox(INDENT);
self.zerobreak();
for element in expr.elems.iter().delimited() {
- self.expr(&element);
+ self.expr(&element, FixupContext::NONE);
self.trailing_comma(element.is_last);
}
self.offset(-INDENT);
@@ -127,13 +179,21 @@
self.word("]");
}
- fn expr_assign(&mut self, expr: &ExprAssign) {
+ fn expr_assign(&mut self, expr: &ExprAssign, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.left,
+ false,
+ false,
+ Precedence::Assign,
+ );
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
+
self.outer_attrs(&expr.attrs);
self.ibox(0);
- self.expr(&expr.left);
+ self.subexpr(&expr.left, left_prec <= Precedence::Range, left_fixup);
self.word(" = ");
self.neverbreak();
- self.expr(&expr.right);
+ self.expr(&expr.right, right_fixup);
self.end();
}
@@ -148,29 +208,75 @@
self.end();
}
- fn expr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool) {
+ fn expr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
- self.subexpr_await(expr, beginning_of_line);
+ self.prefix_subexpr_await(expr, beginning_of_line, fixup);
self.end();
}
- fn subexpr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool) {
- self.subexpr(&expr.base, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.base);
+ fn prefix_subexpr_await(
+ &mut self,
+ expr: &ExprAwait,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base);
+
+ self.prefix_subexpr(
+ &expr.base,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.base)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".await");
}
- fn expr_binary(&mut self, expr: &ExprBinary) {
+ fn expr_binary(&mut self, expr: &ExprBinary, fixup: FixupContext) {
+ let binop_prec = Precedence::of_binop(&expr.op);
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.left,
+ match &expr.op {
+ BinOp::Sub(_)
+ | BinOp::Mul(_)
+ | BinOp::And(_)
+ | BinOp::Or(_)
+ | BinOp::BitAnd(_)
+ | BinOp::BitOr(_)
+ | BinOp::Shl(_)
+ | BinOp::Lt(_) => true,
+ _ => false,
+ },
+ match &expr.op {
+ BinOp::Shl(_) | BinOp::Lt(_) => true,
+ _ => false,
+ },
+ binop_prec,
+ );
+ let left_needs_group = match binop_prec {
+ Precedence::Assign => left_prec <= Precedence::Range,
+ Precedence::Compare => left_prec <= binop_prec,
+ _ => left_prec < binop_prec,
+ };
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
+ let right_needs_group = binop_prec != Precedence::Assign
+ && right_fixup.rightmost_subexpression_precedence(&expr.right) <= binop_prec;
+
self.outer_attrs(&expr.attrs);
self.ibox(INDENT);
self.ibox(-INDENT);
- self.expr(&expr.left);
+ self.subexpr(&expr.left, left_needs_group, left_fixup);
self.end();
self.space();
self.binary_operator(&expr.op);
self.nbsp();
- self.expr(&expr.right);
+ self.subexpr(&expr.right, right_needs_group, right_fixup);
self.end();
}
@@ -184,7 +290,7 @@
self.end();
}
- fn expr_break(&mut self, expr: &ExprBreak) {
+ fn expr_break(&mut self, expr: &ExprBreak, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("break");
if let Some(lifetime) = &expr.label {
@@ -193,31 +299,62 @@
}
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.subexpr(
+ value,
+ expr.label.is_none() && classify::expr_leading_label(value),
+ fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump),
+ );
}
}
- fn expr_call(&mut self, expr: &ExprCall, beginning_of_line: bool) {
+ fn expr_call(&mut self, expr: &ExprCall, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.func,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+ let needs_paren = if let Expr::Field(func) = &*expr.func {
+ matches!(func.member, Member::Named(_))
+ } else {
+ left_prec < Precedence::Unambiguous
+ };
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.func, beginning_of_line);
+ self.expr_beginning_of_line(&expr.func, needs_paren, beginning_of_line, left_fixup);
self.word("(");
self.call_args(&expr.args);
self.word(")");
}
- fn subexpr_call(&mut self, expr: &ExprCall) {
+ fn prefix_subexpr_call(&mut self, expr: &ExprCall, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.func,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+ let needs_paren = if let Expr::Field(func) = &*expr.func {
+ matches!(func.member, Member::Named(_))
+ } else {
+ left_prec < Precedence::Unambiguous
+ };
+
let beginning_of_line = false;
- self.subexpr(&expr.func, beginning_of_line);
+ self.prefix_subexpr(&expr.func, needs_paren, beginning_of_line, left_fixup);
self.word("(");
self.call_args(&expr.args);
self.word(")");
}
- fn expr_cast(&mut self, expr: &ExprCast) {
+ fn expr_cast(&mut self, expr: &ExprCast, fixup: FixupContext) {
+ let (left_prec, left_fixup) =
+ fixup.leftmost_subexpression_with_operator(&expr.expr, false, false, Precedence::Cast);
+
self.outer_attrs(&expr.attrs);
self.ibox(INDENT);
self.ibox(-INDENT);
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, left_prec < Precedence::Cast, left_fixup);
self.end();
self.space();
self.word("as ");
@@ -225,7 +362,7 @@
self.end();
}
- fn expr_closure(&mut self, expr: &ExprClosure) {
+ fn expr_closure(&mut self, expr: &ExprClosure, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.ibox(0);
if let Some(bound_lifetimes) = &expr.lifetimes {
@@ -275,16 +412,22 @@
pre_break: Some(if okay_to_brace { '{' } else { '(' }),
..BreakToken::default()
});
- self.expr(&expr.body);
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
self.scan_break(BreakToken {
offset: -INDENT,
- pre_break: (okay_to_brace && stmt::add_semi(&expr.body)).then(|| ';'),
- post_break: Some(if okay_to_brace { '}' } else { ')' }),
+ pre_break: (okay_to_brace && stmt::add_semi(&expr.body)).then_some(';'),
+ post_break: if okay_to_brace { "}" } else { ")" },
..BreakToken::default()
});
self.end();
} else {
- self.expr(&expr.body);
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
}
}
ReturnType::Type(_arrow, ty) => {
@@ -298,7 +441,17 @@
self.ty(ty);
self.nbsp();
self.neverbreak();
- self.expr(&expr.body);
+ if matches!(&*expr.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none())
+ {
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
+ } else {
+ self.cbox(INDENT);
+ self.expr_as_small_block(&expr.body, 0);
+ self.end();
+ }
}
}
self.end();
@@ -321,16 +474,33 @@
}
}
- fn expr_field(&mut self, expr: &ExprField, beginning_of_line: bool) {
+ fn expr_field(&mut self, expr: &ExprField, beginning_of_line: bool, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
- self.subexpr_field(expr, beginning_of_line);
+ self.prefix_subexpr_field(expr, beginning_of_line, fixup);
self.end();
}
- fn subexpr_field(&mut self, expr: &ExprField, beginning_of_line: bool) {
- self.subexpr(&expr.base, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.base);
+ fn prefix_subexpr_field(
+ &mut self,
+ expr: &ExprField,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base);
+
+ self.prefix_subexpr(
+ &expr.base,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.base)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".");
self.member(&expr.member);
}
@@ -345,14 +515,14 @@
self.pat(&expr.pat);
self.word(" in ");
self.neverbreak();
- self.wrap_exterior_struct(&expr.expr);
+ self.expr_condition(&expr.expr);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -360,9 +530,9 @@
self.end();
}
- fn expr_group(&mut self, expr: &ExprGroup) {
+ fn expr_group(&mut self, expr: &ExprGroup, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
- self.expr(&expr.expr);
+ self.expr(&expr.expr, fixup);
}
fn expr_if(&mut self, expr: &ExprIf) {
@@ -370,7 +540,7 @@
self.cbox(INDENT);
self.word("if ");
self.cbox(-INDENT);
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.end();
if let Some((_else_token, else_branch)) = &expr.else_branch {
let mut else_branch = &**else_branch;
@@ -381,7 +551,7 @@
Expr::If(expr) => {
self.word("if ");
self.cbox(-INDENT);
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.end();
self.small_block(&expr.then_branch, &[]);
if let Some((_else_token, next)) = &expr.else_branch {
@@ -394,16 +564,7 @@
}
// If not one of the valid expressions to exist in an else
// clause, wrap in a block.
- other => {
- self.word("{");
- self.space();
- self.ibox(INDENT);
- self.expr(other);
- self.end();
- self.space();
- self.offset(-INDENT);
- self.word("}");
- }
+ other => self.expr_as_small_block(other, INDENT),
}
break;
}
@@ -412,8 +573,8 @@
} else {
self.word("{");
self.hardbreak();
- for stmt in &expr.then_branch.stmts {
- self.stmt(stmt);
+ for stmt in expr.then_branch.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.word("}");
@@ -421,18 +582,47 @@
self.end();
}
- fn expr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool) {
+ fn expr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.expr,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.expr, beginning_of_line);
+ self.expr_beginning_of_line(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("[");
- self.expr(&expr.index);
+ self.expr(&expr.index, FixupContext::NONE);
self.word("]");
}
- fn subexpr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool) {
- self.subexpr(&expr.expr, beginning_of_line);
+ fn prefix_subexpr_index(
+ &mut self,
+ expr: &ExprIndex,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.expr,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+
+ self.prefix_subexpr(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("[");
- self.expr(&expr.index);
+ self.expr(&expr.index, FixupContext::NONE);
self.word("]");
}
@@ -441,7 +631,9 @@
self.word("_");
}
- fn expr_let(&mut self, expr: &ExprLet) {
+ fn expr_let(&mut self, expr: &ExprLet, fixup: FixupContext) {
+ let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Let);
+
self.outer_attrs(&expr.attrs);
self.ibox(0);
self.word("let ");
@@ -451,14 +643,7 @@
self.word(" = ");
self.neverbreak();
self.ibox(0);
- let needs_paren = contains_exterior_struct_lit(&expr.expr);
- if needs_paren {
- self.word("(");
- }
- self.expr(&expr.expr);
- if needs_paren {
- self.word(")");
- }
+ self.subexpr(&expr.expr, right_prec < Precedence::Let, right_fixup);
self.end();
self.end();
}
@@ -477,8 +662,8 @@
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -495,7 +680,7 @@
self.outer_attrs(&expr.attrs);
self.ibox(0);
self.word("match ");
- self.wrap_exterior_struct(&expr.expr);
+ self.expr_condition(&expr.expr);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
@@ -511,22 +696,40 @@
self.end();
}
- fn expr_method_call(&mut self, expr: &ExprMethodCall, beginning_of_line: bool) {
+ fn expr_method_call(
+ &mut self,
+ expr: &ExprMethodCall,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
let unindent_call_args = beginning_of_line && is_short_ident(&expr.receiver);
- self.subexpr_method_call(expr, beginning_of_line, unindent_call_args);
+ self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup);
self.end();
}
- fn subexpr_method_call(
+ fn prefix_subexpr_method_call(
&mut self,
expr: &ExprMethodCall,
beginning_of_line: bool,
unindent_call_args: bool,
+ fixup: FixupContext,
) {
- self.subexpr(&expr.receiver, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.receiver);
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.receiver);
+
+ self.prefix_subexpr(
+ &expr.receiver,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.receiver)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".");
self.ident(&expr.method);
if let Some(turbofish) = &expr.turbofish {
@@ -542,7 +745,7 @@
fn expr_paren(&mut self, expr: &ExprParen) {
self.outer_attrs(&expr.attrs);
self.word("(");
- self.expr(&expr.expr);
+ self.expr(&expr.expr, FixupContext::NONE);
self.word(")");
}
@@ -551,52 +754,67 @@
self.qpath(&expr.qself, &expr.path, PathKind::Expr);
}
- pub fn expr_range(&mut self, expr: &ExprRange) {
+ pub fn expr_range(&mut self, expr: &ExprRange, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
if let Some(start) = &expr.start {
- self.expr(start);
+ let (left_prec, left_fixup) =
+ fixup.leftmost_subexpression_with_operator(start, true, false, Precedence::Range);
+ self.subexpr(start, left_prec <= Precedence::Range, left_fixup);
+ } else if self.ends_with('.') {
+ self.nbsp();
}
self.word(match expr.limits {
RangeLimits::HalfOpen(_) => "..",
RangeLimits::Closed(_) => "..=",
});
if let Some(end) = &expr.end {
- self.expr(end);
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
+ let right_prec = right_fixup.rightmost_subexpression_precedence(end);
+ self.subexpr(end, right_prec <= Precedence::Range, right_fixup);
}
}
- fn expr_raw_addr(&mut self, expr: &ExprRawAddr) {
+ fn expr_raw_addr(&mut self, expr: &ExprRawAddr, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.word("&raw ");
self.pointer_mutability(&expr.mutability);
self.nbsp();
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
- fn expr_reference(&mut self, expr: &ExprReference) {
+ fn expr_reference(&mut self, expr: &ExprReference, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.word("&");
if expr.mutability.is_some() {
self.word("mut ");
}
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
fn expr_repeat(&mut self, expr: &ExprRepeat) {
self.outer_attrs(&expr.attrs);
self.word("[");
- self.expr(&expr.expr);
+ self.expr(&expr.expr, FixupContext::NONE);
self.word("; ");
- self.expr(&expr.len);
+ self.expr(&expr.len, FixupContext::NONE);
self.word("]");
}
- fn expr_return(&mut self, expr: &ExprReturn) {
+ fn expr_return(&mut self, expr: &ExprReturn, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("return");
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.expr(
+ value,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
}
@@ -614,7 +832,7 @@
}
if let Some(rest) = &expr.rest {
self.word("..");
- self.expr(rest);
+ self.expr(rest, FixupContext::NONE);
self.space();
}
self.offset(-INDENT);
@@ -622,14 +840,28 @@
self.word("}");
}
- fn expr_try(&mut self, expr: &ExprTry, beginning_of_line: bool) {
+ fn expr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr);
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.expr, beginning_of_line);
+ self.expr_beginning_of_line(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("?");
}
- fn subexpr_try(&mut self, expr: &ExprTry, beginning_of_line: bool) {
- self.subexpr(&expr.expr, beginning_of_line);
+ fn prefix_subexpr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr);
+
+ self.prefix_subexpr(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("?");
}
@@ -647,7 +879,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in expr.elems.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
if expr.elems.len() == 1 {
self.word(",");
self.zerobreak();
@@ -660,10 +892,13 @@
self.word(")");
}
- fn expr_unary(&mut self, expr: &ExprUnary) {
+ fn expr_unary(&mut self, expr: &ExprUnary, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.unary_operator(&expr.op);
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
fn expr_unsafe(&mut self, expr: &ExprUnsafe) {
@@ -675,14 +910,14 @@
}
#[cfg(not(feature = "verbatim"))]
- fn expr_verbatim(&mut self, expr: &TokenStream) {
+ fn expr_verbatim(&mut self, expr: &TokenStream, _fixup: FixupContext) {
if !expr.is_empty() {
unimplemented!("Expr::Verbatim `{}`", expr);
}
}
#[cfg(feature = "verbatim")]
- fn expr_verbatim(&mut self, tokens: &TokenStream) {
+ fn expr_verbatim(&mut self, tokens: &TokenStream, fixup: FixupContext) {
use syn::parse::discouraged::Speculative;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parenthesized, Ident};
@@ -754,7 +989,10 @@
self.outer_attrs(&expr.attrs);
self.word("become");
self.nbsp();
- self.expr(&expr.tail_call);
+ self.expr(
+ &expr.tail_call,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
ExprVerbatim::Builtin(expr) => {
self.outer_attrs(&expr.attrs);
@@ -782,26 +1020,29 @@
self.label(label);
}
self.word("while ");
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
self.word("}");
}
- fn expr_yield(&mut self, expr: &ExprYield) {
+ fn expr_yield(&mut self, expr: &ExprYield, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("yield");
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.expr(
+ value,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
}
@@ -816,7 +1057,7 @@
if field_value.colon_token.is_some() {
self.word(": ");
self.ibox(0);
- self.expr(&field_value.expr);
+ self.expr(&field_value.expr, FixupContext::NONE);
self.end();
}
}
@@ -827,9 +1068,9 @@
self.pat(&arm.pat);
if let Some((_if_token, guard)) = &arm.guard {
self.word(" if ");
- self.expr(guard);
+ self.expr(guard, FixupContext::NONE);
}
- self.word(" =>");
+ self.word(" => ");
let empty_block;
let mut body = &*arm.body;
while let Expr::Block(expr) = body {
@@ -856,7 +1097,6 @@
}
}
if let Expr::Block(body) = body {
- self.nbsp();
if let Some(label) = &body.label {
self.label(label);
}
@@ -865,45 +1105,44 @@
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&body.attrs);
- for stmt in &body.block.stmts {
- self.stmt(stmt);
+ for stmt in body.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
self.word("}");
- self.end();
} else {
- self.nbsp();
self.neverbreak();
self.cbox(INDENT);
+ let okay_to_brace = parseable_as_stmt(body);
self.scan_break(BreakToken {
- pre_break: Some('{'),
+ pre_break: Some(if okay_to_brace { '{' } else { '(' }),
..BreakToken::default()
});
- self.expr_beginning_of_line(body, true);
+ self.expr_beginning_of_line(body, false, true, FixupContext::new_match_arm());
self.scan_break(BreakToken {
offset: -INDENT,
- pre_break: stmt::add_semi(body).then(|| ';'),
- post_break: Some('}'),
- no_break: requires_terminator(body).then(|| ','),
+ pre_break: (okay_to_brace && stmt::add_semi(body)).then_some(';'),
+ post_break: if okay_to_brace { "}" } else { ")," },
+ no_break: classify::requires_comma_to_be_match_arm(body).then_some(','),
..BreakToken::default()
});
self.end();
- self.end();
}
+ self.end();
}
fn call_args(&mut self, args: &Punctuated<Expr, Token![,]>) {
let mut iter = args.iter();
match (iter.next(), iter.next()) {
(Some(expr), None) if is_blocklike(expr) => {
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
}
_ => {
self.cbox(INDENT);
self.zerobreak();
for arg in args.iter().delimited() {
- self.expr(&arg);
+ self.expr(&arg, FixupContext::NONE);
self.trailing_comma(arg.is_last);
}
self.offset(-INDENT);
@@ -920,13 +1159,13 @@
match block.stmts.as_slice() {
[Stmt::Expr(expr, None)] if stmt::break_after(expr) => {
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
self.end();
self.space();
}
_ => {
- for stmt in &block.stmts {
- self.stmt(stmt);
+ for stmt in block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
}
}
@@ -935,6 +1174,17 @@
self.word("}");
}
+ pub fn expr_as_small_block(&mut self, expr: &Expr, indent: isize) {
+ self.word("{");
+ self.space();
+ self.ibox(indent);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
+ self.end();
+ self.space();
+ self.offset(-INDENT);
+ self.word("}");
+ }
+
pub fn member(&mut self, member: &Member) {
match member {
Member::Named(ident) => self.ident(ident),
@@ -1001,123 +1251,6 @@
PointerMutability::Mut(_) => self.word("mut"),
}
}
-
- fn zerobreak_unless_short_ident(&mut self, beginning_of_line: bool, expr: &Expr) {
- if beginning_of_line && is_short_ident(expr) {
- return;
- }
- self.zerobreak();
- }
-}
-
-fn requires_terminator(expr: &Expr) -> bool {
- // see https://github.com/rust-lang/rust/blob/a266f1199/compiler/rustc_ast/src/util/classify.rs#L7-L26
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- 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::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::RawAddr(_)
- | Expr::Reference(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Struct(_)
- | Expr::Try(_)
- | Expr::Tuple(_)
- | Expr::Unary(_)
- | Expr::Verbatim(_)
- | Expr::Yield(_) => true,
-
- _ => true,
- }
-}
-
-// Expressions that syntactically contain an "exterior" struct literal i.e. not
-// surrounded by any parens or other delimiters. For example `X { y: 1 }`, `X {
-// y: 1 }.method()`, `foo == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X
-// { y: 1 }) == foo` does not.
-fn contains_exterior_struct_lit(expr: &Expr) -> bool {
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- Expr::Struct(_) => true,
-
- Expr::Assign(ExprAssign { 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::Cast(ExprCast { expr: e, .. })
- | Expr::Field(ExprField { base: e, .. })
- | Expr::Group(ExprGroup { expr: e, .. })
- | Expr::Index(ExprIndex { expr: e, .. })
- | Expr::MethodCall(ExprMethodCall { receiver: e, .. })
- | Expr::RawAddr(ExprRawAddr { expr: e, .. })
- | Expr::Reference(ExprReference { 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::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,
-
- _ => false,
- }
}
fn needs_newline_if_wrap(expr: &Expr) -> bool {
@@ -1208,7 +1341,6 @@
| Expr::Continue(_)
| Expr::Field(_)
| Expr::ForLoop(_)
- | Expr::Group(_)
| Expr::If(_)
| Expr::Index(_)
| Expr::Infer(_)
@@ -1231,62 +1363,106 @@
| Expr::While(_)
| Expr::Yield(_) => false,
+ Expr::Group(e) => is_blocklike(&e.expr),
+
_ => false,
}
}
+pub fn simple_block(expr: &Expr) -> Option<&ExprBlock> {
+ if let Expr::Block(expr) = expr {
+ if expr.attrs.is_empty() && expr.label.is_none() {
+ return Some(expr);
+ }
+ }
+ None
+}
+
// Expressions for which `$expr` and `{ $expr }` mean the same thing.
//
// This is not the case for all expressions. For example `{} | x | x` has some
// bitwise OR operators while `{ {} |x| x }` has a block followed by a closure.
-fn parseable_as_stmt(expr: &Expr) -> bool {
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- Expr::Array(_)
- | Expr::Async(_)
- | Expr::Block(_)
- | Expr::Break(_)
- | Expr::Closure(_)
- | Expr::Const(_)
- | Expr::Continue(_)
- | Expr::ForLoop(_)
- | Expr::If(_)
- | Expr::Infer(_)
- | Expr::Let(_)
- | Expr::Lit(_)
- | Expr::Loop(_)
- | Expr::Macro(_)
- | Expr::Match(_)
- | Expr::Paren(_)
- | Expr::Path(_)
- | Expr::RawAddr(_)
- | Expr::Reference(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Struct(_)
- | Expr::TryBlock(_)
- | Expr::Tuple(_)
- | Expr::Unary(_)
- | Expr::Unsafe(_)
- | Expr::Verbatim(_)
- | Expr::While(_)
- | Expr::Yield(_) => true,
+fn parseable_as_stmt(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Block(_)
+ | Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::Loop(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::While(_)
+ | Expr::Yield(_) => return true,
- Expr::Assign(expr) => parseable_as_stmt(&expr.left),
- Expr::Await(expr) => parseable_as_stmt(&expr.base),
- Expr::Binary(expr) => requires_terminator(&expr.left) && parseable_as_stmt(&expr.left),
- Expr::Call(expr) => requires_terminator(&expr.func) && parseable_as_stmt(&expr.func),
- Expr::Cast(expr) => requires_terminator(&expr.expr) && parseable_as_stmt(&expr.expr),
- Expr::Field(expr) => parseable_as_stmt(&expr.base),
- Expr::Group(expr) => parseable_as_stmt(&expr.expr),
- Expr::Index(expr) => requires_terminator(&expr.expr) && parseable_as_stmt(&expr.expr),
- Expr::MethodCall(expr) => parseable_as_stmt(&expr.receiver),
- Expr::Range(expr) => match &expr.start {
- None => true,
- Some(start) => requires_terminator(start) && parseable_as_stmt(start),
- },
- Expr::Try(expr) => parseable_as_stmt(&expr.expr),
+ Expr::Let(_) => return false,
- _ => false,
+ Expr::Assign(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.left) {
+ return false;
+ }
+ expr = &e.left;
+ }
+ Expr::Await(e) => expr = &e.base,
+ Expr::Binary(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.left) {
+ return false;
+ }
+ expr = &e.left;
+ }
+ Expr::Call(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.func) {
+ return false;
+ }
+ expr = &e.func;
+ }
+ Expr::Cast(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.expr) {
+ return false;
+ }
+ expr = &e.expr;
+ }
+ Expr::Field(e) => expr = &e.base,
+ Expr::Group(e) => expr = &e.expr,
+ Expr::Index(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.expr) {
+ return false;
+ }
+ expr = &e.expr;
+ }
+ Expr::MethodCall(e) => expr = &e.receiver,
+ Expr::Range(e) => match &e.start {
+ None => return true,
+ Some(start) => {
+ if !classify::requires_semi_to_be_stmt(start) {
+ return false;
+ }
+ expr = start;
+ }
+ },
+ Expr::Try(e) => expr = &e.expr,
+
+ _ => return false,
+ }
}
}
diff --git a/crates/prettyplease/src/fixup.rs b/crates/prettyplease/src/fixup.rs
new file mode 100644
index 0000000..55d3623
--- /dev/null
+++ b/crates/prettyplease/src/fixup.rs
@@ -0,0 +1,673 @@
+use crate::classify;
+use crate::precedence::Precedence;
+use syn::{
+ Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield,
+};
+
+#[derive(Copy, Clone)]
+pub struct FixupContext {
+ previous_operator: Precedence,
+ next_operator: Precedence,
+
+ // Print expression such that it can be parsed back as a statement
+ // consisting of the original expression.
+ //
+ // The effect of this is for binary operators in statement position to set
+ // `leftmost_subexpression_in_stmt` when printing their left-hand operand.
+ //
+ // (match x {}) - 1; // match needs parens when LHS of binary operator
+ //
+ // match x {}; // not when its own statement
+ //
+ stmt: bool,
+
+ // This is the difference between:
+ //
+ // (match x {}) - 1; // subexpression needs parens
+ //
+ // let _ = match x {} - 1; // no parens
+ //
+ // There are 3 distinguishable contexts in which `print_expr` might be
+ // called with the expression `$match` as its argument, where `$match`
+ // represents an expression of kind `ExprKind::Match`:
+ //
+ // - stmt=false leftmost_subexpression_in_stmt=false
+ //
+ // Example: `let _ = $match - 1;`
+ //
+ // No parentheses required.
+ //
+ // - stmt=false leftmost_subexpression_in_stmt=true
+ //
+ // Example: `$match - 1;`
+ //
+ // Must parenthesize `($match)`, otherwise parsing back the output as a
+ // statement would terminate the statement after the closing brace of
+ // the match, parsing `-1;` as a separate statement.
+ //
+ // - stmt=true leftmost_subexpression_in_stmt=false
+ //
+ // Example: `$match;`
+ //
+ // No parentheses required.
+ leftmost_subexpression_in_stmt: bool,
+
+ // Print expression such that it can be parsed as a match arm.
+ //
+ // This is almost equivalent to `stmt`, but the grammar diverges a tiny bit
+ // between statements and match arms when it comes to braced macro calls.
+ // Macro calls with brace delimiter terminate a statement without a
+ // semicolon, but do not terminate a match-arm without comma.
+ //
+ // m! {} - 1; // two statements: a macro call followed by -1 literal
+ //
+ // match () {
+ // _ => m! {} - 1, // binary subtraction operator
+ // }
+ //
+ match_arm: bool,
+
+ // This is almost equivalent to `leftmost_subexpression_in_stmt`, other than
+ // for braced macro calls.
+ //
+ // If we have `m! {} - 1` as an expression, the leftmost subexpression
+ // `m! {}` will need to be parenthesized in the statement case but not the
+ // match-arm case.
+ //
+ // (m! {}) - 1; // subexpression needs parens
+ //
+ // match () {
+ // _ => m! {} - 1, // no parens
+ // }
+ //
+ leftmost_subexpression_in_match_arm: bool,
+
+ // This is the difference between:
+ //
+ // if let _ = (Struct {}) {} // needs parens
+ //
+ // match () {
+ // () if let _ = Struct {} => {} // no parens
+ // }
+ //
+ condition: bool,
+
+ // This is the difference between:
+ //
+ // if break Struct {} == (break) {} // needs parens
+ //
+ // if break break == Struct {} {} // no parens
+ //
+ rightmost_subexpression_in_condition: bool,
+
+ // This is the difference between:
+ //
+ // if break ({ x }).field + 1 {} needs parens
+ //
+ // if break 1 + { x }.field {} // no parens
+ //
+ leftmost_subexpression_in_optional_operand: bool,
+
+ // This is the difference between:
+ //
+ // let _ = (return) - 1; // without paren, this would return -1
+ //
+ // let _ = return + 1; // no paren because '+' cannot begin expr
+ //
+ next_operator_can_begin_expr: bool,
+
+ // This is the difference between:
+ //
+ // let _ = 1 + return 1; // no parens if rightmost subexpression
+ //
+ // let _ = 1 + (return 1) + 1; // needs parens
+ //
+ next_operator_can_continue_expr: bool,
+
+ // This is the difference between:
+ //
+ // let _ = x as u8 + T;
+ //
+ // let _ = (x as u8) < T;
+ //
+ // Without parens, the latter would want to parse `u8<T...` as a type.
+ next_operator_can_begin_generics: bool,
+}
+
+impl FixupContext {
+ /// The default amount of fixing is minimal fixing. Fixups should be turned
+ /// on in a targeted fashion where needed.
+ pub const NONE: Self = FixupContext {
+ previous_operator: Precedence::MIN,
+ next_operator: Precedence::MIN,
+ stmt: false,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: false,
+ condition: false,
+ rightmost_subexpression_in_condition: false,
+ leftmost_subexpression_in_optional_operand: false,
+ next_operator_can_begin_expr: false,
+ next_operator_can_continue_expr: false,
+ next_operator_can_begin_generics: false,
+ };
+
+ /// Create the initial fixup for printing an expression in statement
+ /// position.
+ pub fn new_stmt() -> Self {
+ FixupContext {
+ stmt: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Create the initial fixup for printing an expression as the right-hand
+ /// side of a match arm.
+ pub fn new_match_arm() -> Self {
+ FixupContext {
+ match_arm: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Create the initial fixup for printing an expression as the "condition"
+ /// of an `if` or `while`. There are a few other positions which are
+ /// grammatically equivalent and also use this, such as the iterator
+ /// expression in `for` and the scrutinee in `match`.
+ pub fn new_condition() -> Self {
+ FixupContext {
+ condition: true,
+ rightmost_subexpression_in_condition: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Transform this fixup into the one that should apply when printing the
+ /// leftmost subexpression of the current expression.
+ ///
+ /// The leftmost subexpression is any subexpression that has the same first
+ /// token as the current expression, but has a different last token.
+ ///
+ /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
+ /// leftmost subexpression.
+ ///
+ /// Not every expression has a leftmost subexpression. For example neither
+ /// `-$a` nor `[$a]` have one.
+ pub fn leftmost_subexpression_with_operator(
+ self,
+ expr: &Expr,
+ next_operator_can_begin_expr: bool,
+ next_operator_can_begin_generics: bool,
+ precedence: Precedence,
+ ) -> (Precedence, Self) {
+ let fixup = FixupContext {
+ next_operator: precedence,
+ stmt: false,
+ leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: self.match_arm
+ || self.leftmost_subexpression_in_match_arm,
+ rightmost_subexpression_in_condition: false,
+ next_operator_can_begin_expr,
+ next_operator_can_continue_expr: true,
+ next_operator_can_begin_generics,
+ ..self
+ };
+
+ (fixup.leftmost_subexpression_precedence(expr), fixup)
+ }
+
+ /// Transform this fixup into the one that should apply when printing a
+ /// leftmost subexpression followed by a `.` or `?` token, which confer
+ /// different statement boundary rules compared to other leftmost
+ /// subexpressions.
+ pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) {
+ let fixup = FixupContext {
+ next_operator: Precedence::Unambiguous,
+ stmt: self.stmt || self.leftmost_subexpression_in_stmt,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
+ leftmost_subexpression_in_match_arm: false,
+ rightmost_subexpression_in_condition: false,
+ next_operator_can_begin_expr: false,
+ next_operator_can_continue_expr: true,
+ next_operator_can_begin_generics: false,
+ ..self
+ };
+
+ (fixup.leftmost_subexpression_precedence(expr), fixup)
+ }
+
+ fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
+ if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range {
+ if let Scan::Bailout = scan_right(expr, self, Precedence::MIN, 0, 0) {
+ if scan_left(expr, self) {
+ return Precedence::Unambiguous;
+ }
+ }
+ }
+
+ self.precedence(expr)
+ }
+
+ /// Transform this fixup into the one that should apply when printing the
+ /// rightmost subexpression of the current expression.
+ ///
+ /// The rightmost subexpression is any subexpression that has a different
+ /// first token than the current expression, but has the same last token.
+ ///
+ /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a
+ /// rightmost subexpression.
+ ///
+ /// Not every expression has a rightmost subexpression. For example neither
+ /// `[$b]` nor `$a.f($b)` have one.
+ pub fn rightmost_subexpression(
+ self,
+ expr: &Expr,
+ precedence: Precedence,
+ ) -> (Precedence, Self) {
+ let fixup = self.rightmost_subexpression_fixup(false, false, precedence);
+ (fixup.rightmost_subexpression_precedence(expr), fixup)
+ }
+
+ pub fn rightmost_subexpression_fixup(
+ self,
+ reset_allow_struct: bool,
+ optional_operand: bool,
+ precedence: Precedence,
+ ) -> Self {
+ FixupContext {
+ previous_operator: precedence,
+ stmt: false,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: false,
+ condition: self.condition && !reset_allow_struct,
+ leftmost_subexpression_in_optional_operand: self.condition && optional_operand,
+ ..self
+ }
+ }
+
+ pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
+ let default_prec = self.precedence(expr);
+
+ if match self.previous_operator {
+ Precedence::Assign | Precedence::Let | Precedence::Prefix => {
+ default_prec < self.previous_operator
+ }
+ _ => default_prec <= self.previous_operator,
+ } && match self.next_operator {
+ Precedence::Range | Precedence::Or | Precedence::And => true,
+ _ => !self.next_operator_can_begin_expr,
+ } {
+ if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0)
+ {
+ if scan_left(expr, self) {
+ return Precedence::Prefix;
+ }
+ }
+ }
+
+ default_prec
+ }
+
+ /// Determine whether parentheses are needed around the given expression to
+ /// head off the early termination of a statement or condition.
+ pub fn parenthesize(self, expr: &Expr) -> bool {
+ (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr))
+ || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_)))
+ || (self.leftmost_subexpression_in_match_arm
+ && !classify::requires_comma_to_be_match_arm(expr))
+ || (self.condition && matches!(expr, Expr::Struct(_)))
+ || (self.rightmost_subexpression_in_condition
+ && matches!(
+ expr,
+ Expr::Return(ExprReturn { expr: None, .. })
+ | Expr::Yield(ExprYield { expr: None, .. })
+ ))
+ || (self.rightmost_subexpression_in_condition
+ && !self.condition
+ && matches!(
+ expr,
+ Expr::Break(ExprBreak { expr: None, .. })
+ | Expr::Path(_)
+ | Expr::Range(ExprRange { end: None, .. })
+ ))
+ || (self.leftmost_subexpression_in_optional_operand
+ && matches!(expr, Expr::Block(expr) if expr.attrs.is_empty() && expr.label.is_none()))
+ }
+
+ /// Determines the effective precedence of a subexpression. Some expressions
+ /// have higher or lower precedence when adjacent to particular operators.
+ fn precedence(self, expr: &Expr) -> Precedence {
+ if self.next_operator_can_begin_expr {
+ // Decrease precedence of value-less jumps when followed by an
+ // operator that would otherwise get interpreted as beginning a
+ // value for the jump.
+ if let Expr::Break(ExprBreak { expr: None, .. })
+ | Expr::Return(ExprReturn { expr: None, .. })
+ | Expr::Yield(ExprYield { expr: None, .. }) = expr
+ {
+ return Precedence::Jump;
+ }
+ }
+
+ if !self.next_operator_can_continue_expr {
+ match expr {
+ // Increase precedence of expressions that extend to the end of
+ // current statement or group.
+ Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Let(_)
+ | Expr::Return(_)
+ | Expr::Yield(_) => {
+ return Precedence::Prefix;
+ }
+ Expr::Range(e) if e.start.is_none() => return Precedence::Prefix,
+ _ => {}
+ }
+ }
+
+ if self.next_operator_can_begin_generics {
+ if let Expr::Cast(cast) = expr {
+ if classify::trailing_unparameterized_path(&cast.ty) {
+ return Precedence::MIN;
+ }
+ }
+ }
+
+ Precedence::of(expr)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq)]
+enum Scan {
+ Fail,
+ Bailout,
+ Consume,
+}
+
+fn scan_left(expr: &Expr, fixup: FixupContext) -> bool {
+ match expr {
+ Expr::Assign(_) => fixup.previous_operator <= Precedence::Assign,
+ Expr::Binary(e) => match Precedence::of_binop(&e.op) {
+ Precedence::Assign => fixup.previous_operator <= Precedence::Assign,
+ binop_prec => fixup.previous_operator < binop_prec,
+ },
+ Expr::Cast(_) => fixup.previous_operator < Precedence::Cast,
+ Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign,
+ _ => true,
+ }
+}
+
+fn scan_right(
+ expr: &Expr,
+ fixup: FixupContext,
+ precedence: Precedence,
+ fail_offset: u8,
+ bailout_offset: u8,
+) -> Scan {
+ let consume_by_precedence = if match precedence {
+ Precedence::Assign | Precedence::Compare => precedence <= fixup.next_operator,
+ _ => precedence < fixup.next_operator,
+ } || fixup.next_operator == Precedence::MIN
+ {
+ Scan::Consume
+ } else {
+ Scan::Bailout
+ };
+ if fixup.parenthesize(expr) {
+ return consume_by_precedence;
+ }
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Assign(e) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset >= 2,
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
+ let scan = scan_right(
+ &e.right,
+ right_fixup,
+ Precedence::Assign,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ 1,
+ );
+ if let Scan::Bailout | Scan::Consume = scan {
+ Scan::Consume
+ } else if let Precedence::Unambiguous = fixup.next_operator {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::Binary(e) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => {
+ fail_offset >= 2
+ && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
+ }
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let binop_prec = Precedence::of_binop(&e.op);
+ if binop_prec == Precedence::Compare && fixup.next_operator == Precedence::Compare {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
+ let scan = scan_right(
+ &e.right,
+ right_fixup,
+ binop_prec,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ consume_by_precedence as u8 - Scan::Bailout as u8,
+ );
+ match scan {
+ Scan::Fail => {}
+ Scan::Bailout => return consume_by_precedence,
+ Scan::Consume => return Scan::Consume,
+ }
+ let right_needs_group = binop_prec != Precedence::Assign
+ && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec;
+ if right_needs_group {
+ consume_by_precedence
+ } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::RawAddr(ExprRawAddr { expr, .. })
+ | Expr::Reference(ExprReference { expr, .. })
+ | Expr::Unary(ExprUnary { expr, .. }) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => {
+ fail_offset >= 2
+ && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
+ }
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix);
+ let scan = scan_right(
+ expr,
+ right_fixup,
+ precedence,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ consume_by_precedence as u8 - Scan::Bailout as u8,
+ );
+ match scan {
+ Scan::Fail => {}
+ Scan::Bailout => return consume_by_precedence,
+ Scan::Consume => return Scan::Consume,
+ }
+ if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix {
+ consume_by_precedence
+ } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::Range(e) => match &e.end {
+ Some(end) => {
+ if fail_offset >= 2 {
+ return Scan::Consume;
+ }
+ let right_fixup =
+ fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
+ let scan = scan_right(
+ end,
+ right_fixup,
+ Precedence::Range,
+ fail_offset,
+ match fixup.next_operator {
+ Precedence::Assign | Precedence::Range => 0,
+ _ => 1,
+ },
+ );
+ if match (scan, fixup.next_operator) {
+ (Scan::Fail, _) => false,
+ (Scan::Bailout, Precedence::Assign | Precedence::Range) => false,
+ (Scan::Bailout | Scan::Consume, _) => true,
+ } {
+ return Scan::Consume;
+ }
+ if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range {
+ Scan::Consume
+ } else {
+ Scan::Fail
+ }
+ }
+ None => {
+ if fixup.next_operator_can_begin_expr {
+ Scan::Consume
+ } else {
+ Scan::Fail
+ }
+ }
+ },
+ Expr::Break(e) => match &e.expr {
+ Some(value) => {
+ if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump);
+ match scan_right(value, right_fixup, Precedence::Jump, 1, 1) {
+ Scan::Fail => Scan::Bailout,
+ Scan::Bailout | Scan::Consume => Scan::Consume,
+ }
+ }
+ None => match fixup.next_operator {
+ Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
+ _ => Scan::Consume,
+ },
+ },
+ Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr {
+ Some(e) => {
+ if bailout_offset >= 1 {
+ return Scan::Consume;
+ }
+ let right_fixup =
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump);
+ match scan_right(e, right_fixup, Precedence::Jump, 1, 1) {
+ Scan::Fail => Scan::Bailout,
+ Scan::Bailout | Scan::Consume => Scan::Consume,
+ }
+ }
+ None => match fixup.next_operator {
+ Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
+ _ => Scan::Consume,
+ },
+ },
+ Expr::Closure(_) => Scan::Consume,
+ Expr::Let(e) => {
+ if bailout_offset >= 1 {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Let);
+ let scan = scan_right(
+ &e.expr,
+ right_fixup,
+ Precedence::Let,
+ 1,
+ if fixup.next_operator < Precedence::Let {
+ 0
+ } else {
+ 1
+ },
+ );
+ match scan {
+ Scan::Fail | Scan::Bailout if fixup.next_operator < Precedence::Let => {
+ return Scan::Bailout;
+ }
+ Scan::Consume => return Scan::Consume,
+ _ => {}
+ }
+ if right_fixup.rightmost_subexpression_precedence(&e.expr) < Precedence::Let {
+ Scan::Consume
+ } else if let Scan::Fail = scan {
+ Scan::Bailout
+ } else {
+ Scan::Consume
+ }
+ }
+ Expr::Group(e) => scan_right(&e.expr, fixup, precedence, fail_offset, bailout_offset),
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Await(_)
+ | Expr::Block(_)
+ | Expr::Call(_)
+ | Expr::Cast(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::Loop(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Repeat(_)
+ | Expr::Struct(_)
+ | Expr::Try(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::While(_) => match fixup.next_operator {
+ Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
+ _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
+ Scan::Fail
+ }
+ _ => consume_by_precedence,
+ },
+
+ _ => match fixup.next_operator {
+ Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
+ _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
+ Scan::Fail
+ }
+ _ => consume_by_precedence,
+ },
+ }
+}
diff --git a/crates/prettyplease/src/generics.rs b/crates/prettyplease/src/generics.rs
index 6145345..3f225b8 100644
--- a/crates/prettyplease/src/generics.rs
+++ b/crates/prettyplease/src/generics.rs
@@ -5,7 +5,7 @@
use proc_macro2::TokenStream;
use std::ptr;
use syn::{
- BoundLifetimes, CapturedParam, ConstParam, GenericParam, Generics, LifetimeParam,
+ BoundLifetimes, CapturedParam, ConstParam, Expr, GenericParam, Generics, LifetimeParam,
PreciseCapture, PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, TypeParam,
TypeParamBound, WhereClause, WherePredicate,
};
@@ -209,7 +209,7 @@
self.ty(&const_param.ty);
if let Some(default) = &const_param.default {
self.word(" = ");
- self.expr(default);
+ self.const_argument(default);
}
}
@@ -357,4 +357,27 @@
_ => unimplemented!("unknown CapturedParam"),
}
}
+
+ pub fn const_argument(&mut self, expr: &Expr) {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))]
+ Expr::Lit(expr) => self.expr_lit(expr),
+
+ Expr::Path(expr)
+ if expr.attrs.is_empty()
+ && expr.qself.is_none()
+ && expr.path.get_ident().is_some() =>
+ {
+ self.expr_path(expr);
+ }
+
+ Expr::Block(expr) => self.expr_block(expr),
+
+ _ => {
+ self.cbox(INDENT);
+ self.expr_as_small_block(expr, 0);
+ self.end();
+ }
+ }
+ }
}
diff --git a/crates/prettyplease/src/item.rs b/crates/prettyplease/src/item.rs
index 8254429..4e9d95d 100644
--- a/crates/prettyplease/src/item.rs
+++ b/crates/prettyplease/src/item.rs
@@ -1,5 +1,7 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
+use crate::mac;
use crate::path::PathKind;
use crate::INDENT;
use proc_macro2::TokenStream;
@@ -47,7 +49,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.expr);
+ self.expr(&item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -100,8 +102,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&item.attrs);
- for stmt in &item.block.stmts {
- self.stmt(stmt);
+ for stmt in item.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -168,7 +170,7 @@
fn item_macro(&mut self, item: &ItemMacro) {
self.outer_attrs(&item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&item.mac.delimiter);
self.mac(&item.mac, item.ident.as_ref(), semicolon);
self.hardbreak();
}
@@ -210,7 +212,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.expr);
+ self.expr(&item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -836,7 +838,7 @@
fn foreign_item_macro(&mut self, foreign_item: &ForeignItemMacro) {
self.outer_attrs(&foreign_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&foreign_item.mac.delimiter);
self.mac(&foreign_item.mac, None, semicolon);
self.hardbreak();
}
@@ -961,7 +963,7 @@
if let Some((_eq_token, default)) = &trait_item.default {
self.word(" = ");
self.neverbreak();
- self.expr(default);
+ self.expr(default, FixupContext::NONE);
}
self.word(";");
self.end();
@@ -981,8 +983,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&trait_item.attrs);
- for stmt in &block.stmts {
- self.stmt(stmt);
+ for stmt in block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1023,7 +1025,7 @@
fn trait_item_macro(&mut self, trait_item: &TraitItemMacro) {
self.outer_attrs(&trait_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&trait_item.mac.delimiter);
self.mac(&trait_item.mac, None, semicolon);
self.hardbreak();
}
@@ -1158,7 +1160,7 @@
self.ty(&impl_item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&impl_item.expr);
+ self.expr(&impl_item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -1180,8 +1182,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&impl_item.attrs);
- for stmt in &impl_item.block.stmts {
- self.stmt(stmt);
+ for stmt in impl_item.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1211,7 +1213,7 @@
fn impl_item_macro(&mut self, impl_item: &ImplItemMacro) {
self.outer_attrs(&impl_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&impl_item.mac.delimiter);
self.mac(&impl_item.mac, None, semicolon);
self.hardbreak();
}
@@ -1421,6 +1423,7 @@
#[cfg(feature = "verbatim")]
mod verbatim {
use crate::algorithm::Printer;
+ use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::INDENT;
use syn::ext::IdentExt;
@@ -1707,7 +1710,7 @@
self.word(" = ");
self.neverbreak();
self.ibox(-INDENT);
- self.expr(value);
+ self.expr(value, FixupContext::NONE);
self.end();
}
self.where_clause_oneline_semi(&item.generics.where_clause);
@@ -1728,8 +1731,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&item.attrs);
- for stmt in body {
- self.stmt(stmt);
+ for stmt in body.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1756,7 +1759,7 @@
if let Some(expr) = &item.expr {
self.word(" = ");
self.neverbreak();
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
}
self.word(";");
self.end();
diff --git a/crates/prettyplease/src/lib.rs b/crates/prettyplease/src/lib.rs
index b970d8f..25f0250 100644
--- a/crates/prettyplease/src/lib.rs
+++ b/crates/prettyplease/src/lib.rs
@@ -320,8 +320,9 @@
//! 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.2.25")]
+#![doc(html_root_url = "https://docs.rs/prettyplease/0.2.29")]
#![allow(
+ clippy::bool_to_int_with_if,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::derive_partial_eq_without_eq,
@@ -336,6 +337,7 @@
clippy::needless_pass_by_value,
clippy::ref_option,
clippy::similar_names,
+ clippy::struct_excessive_bools,
clippy::too_many_lines,
clippy::unused_self,
clippy::vec_init_then_push
@@ -344,10 +346,12 @@
mod algorithm;
mod attr;
+mod classify;
mod convenience;
mod data;
mod expr;
mod file;
+mod fixup;
mod generics;
mod item;
mod iter;
@@ -356,6 +360,7 @@
mod mac;
mod pat;
mod path;
+mod precedence;
mod ring;
mod stmt;
mod token;
diff --git a/crates/prettyplease/src/mac.rs b/crates/prettyplease/src/mac.rs
index ebc56c7..230a8d4 100644
--- a/crates/prettyplease/src/mac.rs
+++ b/crates/prettyplease/src/mac.rs
@@ -41,10 +41,7 @@
}
self.word(close);
if semicolon {
- match mac.delimiter {
- MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"),
- MacroDelimiter::Brace(_) => {}
- }
+ self.word(";");
}
}
@@ -179,6 +176,7 @@
(_, Token::Ident(ident)) if !is_keyword(ident) => {
(state != Dot && state != Colon2, Ident)
}
+ (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other),
(_, Token::Literal(_)) => (state != Dot, Ident),
(_, Token::Punct(',' | ';', _)) => (false, Other),
(_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
@@ -211,6 +209,13 @@
}
}
+pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool {
+ match delimiter {
+ MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true,
+ MacroDelimiter::Brace(_) => false,
+ }
+}
+
fn is_keyword(ident: &Ident) -> bool {
match ident.to_string().as_str() {
"as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
@@ -224,6 +229,7 @@
#[cfg(feature = "verbatim")]
mod standard_library {
use crate::algorithm::Printer;
+ use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -543,7 +549,7 @@
self.word("(");
self.cbox(INDENT);
self.zerobreak();
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
self.zerobreak();
self.offset(-INDENT);
self.end();
@@ -554,7 +560,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in exprs.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
self.trailing_comma(elem.is_last);
}
self.offset(-INDENT);
@@ -570,14 +576,14 @@
self.word("(");
self.cbox(INDENT);
self.zerobreak();
- self.expr(&matches.expression);
+ self.expr(&matches.expression, FixupContext::NONE);
self.word(",");
self.space();
self.pat(&matches.pattern);
if let Some(guard) = &matches.guard {
self.space();
self.word("if ");
- self.expr(guard);
+ self.expr(guard, FixupContext::NONE);
}
self.zerobreak();
self.offset(-INDENT);
@@ -598,7 +604,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.init);
+ self.expr(&item.init, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -613,7 +619,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in vec.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
self.trailing_comma(elem.is_last);
}
self.offset(-INDENT);
@@ -624,10 +630,10 @@
self.word("[");
self.cbox(INDENT);
self.zerobreak();
- self.expr(elem);
+ self.expr(elem, FixupContext::NONE);
self.word(";");
self.space();
- self.expr(n);
+ self.expr(n, FixupContext::NONE);
self.zerobreak();
self.offset(-INDENT);
self.end();
diff --git a/crates/prettyplease/src/pat.rs b/crates/prettyplease/src/pat.rs
index aa1806e..23a38cb 100644
--- a/crates/prettyplease/src/pat.rs
+++ b/crates/prettyplease/src/pat.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -19,7 +20,7 @@
Pat::Or(pat) => self.pat_or(pat),
Pat::Paren(pat) => self.pat_paren(pat),
Pat::Path(pat) => self.expr_path(pat),
- Pat::Range(pat) => self.expr_range(pat),
+ Pat::Range(pat) => self.expr_range(pat, FixupContext::NONE),
Pat::Reference(pat) => self.pat_reference(pat),
Pat::Rest(pat) => self.pat_rest(pat),
Pat::Slice(pat) => self.pat_slice(pat),
diff --git a/crates/prettyplease/src/path.rs b/crates/prettyplease/src/path.rs
index 2ae94b3..44428cc 100644
--- a/crates/prettyplease/src/path.rs
+++ b/crates/prettyplease/src/path.rs
@@ -3,7 +3,7 @@
use crate::INDENT;
use std::ptr;
use syn::{
- AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, Expr, GenericArgument,
+ AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
};
@@ -50,20 +50,7 @@
#![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime),
GenericArgument::Type(ty) => self.ty(ty),
- GenericArgument::Const(expr) => {
- match expr {
- #![cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))]
- Expr::Lit(expr) => self.expr_lit(expr),
- Expr::Block(expr) => self.expr_block(expr),
- // ERROR CORRECTION: Add braces to make sure that the
- // generated code is valid.
- _ => {
- self.word("{");
- self.expr(expr);
- self.word("}");
- }
- }
- }
+ GenericArgument::Const(expr) => self.const_argument(expr),
GenericArgument::AssocType(assoc) => self.assoc_type(assoc),
GenericArgument::AssocConst(assoc) => self.assoc_const(assoc),
GenericArgument::Constraint(constraint) => self.constraint(constraint),
@@ -136,7 +123,7 @@
self.angle_bracketed_generic_arguments(generics, PathKind::Type);
}
self.word(" = ");
- self.expr(&assoc.value);
+ self.const_argument(&assoc.value);
}
fn constraint(&mut self, constraint: &Constraint) {
diff --git a/crates/prettyplease/src/precedence.rs b/crates/prettyplease/src/precedence.rs
new file mode 100644
index 0000000..03117d5
--- /dev/null
+++ b/crates/prettyplease/src/precedence.rs
@@ -0,0 +1,148 @@
+use syn::{
+ AttrStyle, Attribute, BinOp, Expr, ExprArray, ExprAsync, ExprAwait, ExprBlock, ExprBreak,
+ ExprCall, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprInfer,
+ ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRepeat,
+ ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnsafe, ExprWhile, ExprYield,
+ ReturnType,
+};
+
+// Reference: https://doc.rust-lang.org/reference/expressions.html#expression-precedence
+#[derive(Copy, Clone, PartialEq, PartialOrd)]
+pub enum Precedence {
+ // return, break, closures
+ Jump,
+ // = += -= *= /= %= &= |= ^= <<= >>=
+ Assign,
+ // .. ..=
+ Range,
+ // ||
+ Or,
+ // &&
+ And,
+ // let
+ Let,
+ // == != < > <= >=
+ Compare,
+ // |
+ BitOr,
+ // ^
+ BitXor,
+ // &
+ BitAnd,
+ // << >>
+ Shift,
+ // + -
+ Sum,
+ // * / %
+ Product,
+ // as
+ Cast,
+ // unary - * ! & &mut
+ Prefix,
+ // paths, loops, function calls, array indexing, field expressions, method calls
+ Unambiguous,
+}
+
+impl Precedence {
+ pub(crate) const MIN: Self = Precedence::Jump;
+
+ pub(crate) fn of_binop(op: &BinOp) -> Self {
+ match op {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ BinOp::Add(_) | BinOp::Sub(_) => Precedence::Sum,
+ BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) => Precedence::Product,
+ BinOp::And(_) => Precedence::And,
+ BinOp::Or(_) => Precedence::Or,
+ BinOp::BitXor(_) => Precedence::BitXor,
+ BinOp::BitAnd(_) => Precedence::BitAnd,
+ BinOp::BitOr(_) => Precedence::BitOr,
+ BinOp::Shl(_) | BinOp::Shr(_) => Precedence::Shift,
+
+ BinOp::Eq(_)
+ | BinOp::Lt(_)
+ | BinOp::Le(_)
+ | BinOp::Ne(_)
+ | BinOp::Ge(_)
+ | BinOp::Gt(_) => Precedence::Compare,
+
+ BinOp::AddAssign(_)
+ | BinOp::SubAssign(_)
+ | BinOp::MulAssign(_)
+ | BinOp::DivAssign(_)
+ | BinOp::RemAssign(_)
+ | BinOp::BitXorAssign(_)
+ | BinOp::BitAndAssign(_)
+ | BinOp::BitOrAssign(_)
+ | BinOp::ShlAssign(_)
+ | BinOp::ShrAssign(_) => Precedence::Assign,
+
+ _ => Precedence::MIN,
+ }
+ }
+
+ pub(crate) fn of(e: &Expr) -> Self {
+ fn prefix_attrs(attrs: &[Attribute]) -> Precedence {
+ for attr in attrs {
+ if let AttrStyle::Outer = attr.style {
+ return Precedence::Prefix;
+ }
+ }
+ Precedence::Unambiguous
+ }
+
+ match e {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Closure(e) => match e.output {
+ ReturnType::Default => Precedence::Jump,
+ ReturnType::Type(..) => prefix_attrs(&e.attrs),
+ },
+
+ Expr::Break(ExprBreak { expr, .. })
+ | Expr::Return(ExprReturn { expr, .. })
+ | Expr::Yield(ExprYield { expr, .. }) => match expr {
+ Some(_) => Precedence::Jump,
+ None => Precedence::Unambiguous,
+ },
+
+ Expr::Assign(_) => Precedence::Assign,
+ Expr::Range(_) => Precedence::Range,
+ Expr::Binary(e) => Precedence::of_binop(&e.op),
+ Expr::Let(_) => Precedence::Let,
+ Expr::Cast(_) => Precedence::Cast,
+ Expr::RawAddr(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
+
+ Expr::Array(ExprArray { attrs, .. })
+ | Expr::Async(ExprAsync { attrs, .. })
+ | Expr::Await(ExprAwait { attrs, .. })
+ | Expr::Block(ExprBlock { attrs, .. })
+ | Expr::Call(ExprCall { attrs, .. })
+ | Expr::Const(ExprConst { attrs, .. })
+ | Expr::Continue(ExprContinue { attrs, .. })
+ | Expr::Field(ExprField { attrs, .. })
+ | Expr::ForLoop(ExprForLoop { attrs, .. })
+ | Expr::If(ExprIf { attrs, .. })
+ | Expr::Index(ExprIndex { attrs, .. })
+ | Expr::Infer(ExprInfer { attrs, .. })
+ | Expr::Lit(ExprLit { attrs, .. })
+ | Expr::Loop(ExprLoop { attrs, .. })
+ | Expr::Macro(ExprMacro { attrs, .. })
+ | Expr::Match(ExprMatch { attrs, .. })
+ | Expr::MethodCall(ExprMethodCall { attrs, .. })
+ | Expr::Paren(ExprParen { attrs, .. })
+ | Expr::Path(ExprPath { attrs, .. })
+ | Expr::Repeat(ExprRepeat { attrs, .. })
+ | Expr::Struct(ExprStruct { attrs, .. })
+ | Expr::Try(ExprTry { attrs, .. })
+ | Expr::TryBlock(ExprTryBlock { attrs, .. })
+ | Expr::Tuple(ExprTuple { attrs, .. })
+ | Expr::Unsafe(ExprUnsafe { attrs, .. })
+ | Expr::While(ExprWhile { attrs, .. }) => prefix_attrs(attrs),
+
+ Expr::Group(e) => Precedence::of(&e.expr),
+
+ Expr::Verbatim(_) => Precedence::Unambiguous,
+
+ _ => Precedence::Unambiguous,
+ }
+ }
+}
diff --git a/crates/prettyplease/src/ring.rs b/crates/prettyplease/src/ring.rs
index aff9258..882a988 100644
--- a/crates/prettyplease/src/ring.rs
+++ b/crates/prettyplease/src/ring.rs
@@ -1,5 +1,5 @@
use std::collections::VecDeque;
-use std::ops::{Index, IndexMut};
+use std::ops::{Index, IndexMut, Range};
pub struct RingBuffer<T> {
data: VecDeque<T>,
@@ -33,8 +33,8 @@
self.data.clear();
}
- pub fn index_of_first(&self) -> usize {
- self.offset
+ pub fn index_range(&self) -> Range<usize> {
+ self.offset..self.offset + self.data.len()
}
pub fn first(&self) -> &T {
diff --git a/crates/prettyplease/src/stmt.rs b/crates/prettyplease/src/stmt.rs
index 2b97da6..ce58200 100644
--- a/crates/prettyplease/src/stmt.rs
+++ b/crates/prettyplease/src/stmt.rs
@@ -1,9 +1,13 @@
use crate::algorithm::Printer;
+use crate::classify;
+use crate::expr;
+use crate::fixup::FixupContext;
+use crate::mac;
use crate::INDENT;
use syn::{BinOp, Expr, Stmt};
impl Printer {
- pub fn stmt(&mut self, stmt: &Stmt) {
+ pub fn stmt(&mut self, stmt: &Stmt, is_last: bool) {
match stmt {
Stmt::Local(local) => {
self.outer_attrs(&local.attrs);
@@ -13,32 +17,26 @@
if let Some(local_init) = &local.init {
self.word(" = ");
self.neverbreak();
- self.expr(&local_init.expr);
+ self.subexpr(
+ &local_init.expr,
+ local_init.diverge.is_some()
+ && classify::expr_trailing_brace(&local_init.expr),
+ FixupContext::NONE,
+ );
if let Some((_else, diverge)) = &local_init.diverge {
self.space();
self.word("else ");
self.end();
self.neverbreak();
- if let Expr::Block(expr) = diverge.as_ref() {
- self.cbox(INDENT);
+ self.cbox(INDENT);
+ if let Some(expr) = expr::simple_block(diverge) {
self.small_block(&expr.block, &[]);
- self.end();
} else {
- self.word("{");
- self.space();
- self.ibox(INDENT);
- self.expr(diverge);
- self.end();
- self.space();
- self.offset(-INDENT);
- self.word("}");
+ self.expr_as_small_block(diverge, INDENT);
}
- } else {
- self.end();
}
- } else {
- self.end();
}
+ self.end();
self.word(";");
self.hardbreak();
}
@@ -46,14 +44,14 @@
Stmt::Expr(expr, None) => {
if break_after(expr) {
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
if add_semi(expr) {
self.word(";");
}
self.end();
self.hardbreak();
} else {
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
}
}
Stmt::Expr(expr, Some(_semi)) => {
@@ -63,7 +61,7 @@
}
}
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
if !remove_semi(expr) {
self.word(";");
}
@@ -72,7 +70,8 @@
}
Stmt::Macro(stmt) => {
self.outer_attrs(&stmt.attrs);
- let semicolon = true;
+ let semicolon = stmt.semi_token.is_some()
+ || !is_last && mac::requires_semi(&stmt.mac.delimiter);
self.mac(&stmt.mac, None, semicolon);
self.hardbreak();
}
diff --git a/crates/prettyplease/src/ty.rs b/crates/prettyplease/src/ty.rs
index ec2cd0a..7f92821 100644
--- a/crates/prettyplease/src/ty.rs
+++ b/crates/prettyplease/src/ty.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -36,7 +37,7 @@
self.word("[");
self.ty(&ty.elem);
self.word("; ");
- self.expr(&ty.len);
+ self.expr(&ty.len, FixupContext::NONE);
self.word("]");
}
diff --git a/crates/prettyplease/tests/test.rs b/crates/prettyplease/tests/test.rs
index 04004a7..aa6b849 100644
--- a/crates/prettyplease/tests/test.rs
+++ b/crates/prettyplease/tests/test.rs
@@ -20,7 +20,7 @@
},
indoc! {"
fn main() {
- if (Struct {} == Struct {}) {}
+ if (Struct {}) == (Struct {}) {}
}
"},
);
@@ -39,17 +39,11 @@
}
}
},
- // FIXME: no parens around `Struct {}` because anything until the `=>`
- // is considered part of the match guard expression. Parsing of the
- // expression is not terminated by `{` in that position.
- //
- // FIXME: the `true && false` needs parens. Otherwise the precedence is
- // `(let _ = true) && false` which means something different.
indoc! {"
fn main() {
match () {
- () if let _ = (Struct {}) => {}
- () if let _ = true && false => {}
+ () if let _ = Struct {} => {}
+ () if let _ = (true && false) => {}
}
}
"},
diff --git a/crates/prettyplease/tests/test_precedence.rs b/crates/prettyplease/tests/test_precedence.rs
new file mode 100644
index 0000000..f1eec23
--- /dev/null
+++ b/crates/prettyplease/tests/test_precedence.rs
@@ -0,0 +1,900 @@
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::ToTokens as _;
+use std::mem;
+use std::process::ExitCode;
+use syn::punctuated::Punctuated;
+use syn::visit_mut::{self, VisitMut};
+use syn::{
+ token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, ExprAssign,
+ ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure,
+ ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop,
+ ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference,
+ ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprUnary, ExprUnsafe, ExprWhile, ExprYield,
+ File, GenericArgument, Generics, Item, ItemConst, Label, Lifetime, LifetimeParam, Lit, LitInt,
+ Macro, MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment,
+ PointerMutability, QSelf, RangeLimits, ReturnType, Stmt, StmtMacro, Token, Type, TypeInfer,
+ TypeParam, TypePath, UnOp, Visibility,
+};
+
+struct FlattenParens;
+
+impl VisitMut for FlattenParens {
+ fn visit_expr_mut(&mut self, e: &mut Expr) {
+ while let Expr::Paren(paren) = e {
+ *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER);
+ }
+ visit_mut::visit_expr_mut(self, e);
+ }
+}
+
+struct AsIfPrinted;
+
+impl VisitMut for AsIfPrinted {
+ fn visit_generics_mut(&mut self, generics: &mut Generics) {
+ if generics.params.is_empty() {
+ generics.lt_token = None;
+ generics.gt_token = None;
+ }
+ if let Some(where_clause) = &generics.where_clause {
+ if where_clause.predicates.is_empty() {
+ generics.where_clause = None;
+ }
+ }
+ visit_mut::visit_generics_mut(self, generics);
+ }
+
+ fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) {
+ if param.bounds.is_empty() {
+ param.colon_token = None;
+ }
+ visit_mut::visit_lifetime_param_mut(self, param);
+ }
+
+ fn visit_stmt_mut(&mut self, stmt: &mut Stmt) {
+ if let Stmt::Expr(expr, semi) = stmt {
+ if let Expr::Macro(e) = expr {
+ if match e.mac.delimiter {
+ MacroDelimiter::Brace(_) => true,
+ MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(),
+ } {
+ let expr = match mem::replace(expr, Expr::PLACEHOLDER) {
+ Expr::Macro(expr) => expr,
+ _ => unreachable!(),
+ };
+ *stmt = Stmt::Macro(StmtMacro {
+ attrs: expr.attrs,
+ mac: expr.mac,
+ semi_token: *semi,
+ });
+ }
+ }
+ }
+ visit_mut::visit_stmt_mut(self, stmt);
+ }
+
+ fn visit_type_param_mut(&mut self, param: &mut TypeParam) {
+ if param.bounds.is_empty() {
+ param.colon_token = None;
+ }
+ visit_mut::visit_type_param_mut(self, param);
+ }
+}
+
+#[test]
+fn test_permutations() -> ExitCode {
+ fn iter(depth: usize, f: &mut dyn FnMut(Expr)) {
+ let span = Span::call_site();
+
+ // Expr::Path
+ f(Expr::Path(ExprPath {
+ // `x`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ }));
+ if false {
+ f(Expr::Path(ExprPath {
+ // `x::<T>`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path {
+ leading_colon: None,
+ segments: Punctuated::from_iter([PathSegment {
+ ident: Ident::new("x", span),
+ arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
+ colon2_token: Some(Token),
+ lt_token: Token,
+ args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
+ TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ },
+ ))]),
+ gt_token: Token,
+ }),
+ }]),
+ },
+ }));
+ f(Expr::Path(ExprPath {
+ // `<T as Trait>::CONST`
+ attrs: Vec::new(),
+ qself: Some(QSelf {
+ lt_token: Token,
+ ty: Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ position: 1,
+ as_token: Some(Token),
+ gt_token: Token,
+ }),
+ path: Path {
+ leading_colon: None,
+ segments: Punctuated::from_iter([
+ PathSegment::from(Ident::new("Trait", span)),
+ PathSegment::from(Ident::new("CONST", span)),
+ ]),
+ },
+ }));
+ }
+
+ let depth = match depth.checked_sub(1) {
+ Some(depth) => depth,
+ None => return,
+ };
+
+ // Expr::Assign
+ iter(depth, &mut |expr| {
+ iter(0, &mut |simple| {
+ f(Expr::Assign(ExprAssign {
+ // `x = $expr`
+ attrs: Vec::new(),
+ left: Box::new(simple.clone()),
+ eq_token: Token,
+ right: Box::new(expr.clone()),
+ }));
+ f(Expr::Assign(ExprAssign {
+ // `$expr = x`
+ attrs: Vec::new(),
+ left: Box::new(expr.clone()),
+ eq_token: Token,
+ right: Box::new(simple),
+ }));
+ });
+ });
+
+ // Expr::Binary
+ iter(depth, &mut |expr| {
+ iter(0, &mut |simple| {
+ for op in [
+ BinOp::Add(Token),
+ //BinOp::Sub(Token),
+ //BinOp::Mul(Token),
+ //BinOp::Div(Token),
+ //BinOp::Rem(Token),
+ //BinOp::And(Token),
+ //BinOp::Or(Token),
+ //BinOp::BitXor(Token),
+ //BinOp::BitAnd(Token),
+ //BinOp::BitOr(Token),
+ //BinOp::Shl(Token),
+ //BinOp::Shr(Token),
+ //BinOp::Eq(Token),
+ BinOp::Lt(Token),
+ //BinOp::Le(Token),
+ //BinOp::Ne(Token),
+ //BinOp::Ge(Token),
+ //BinOp::Gt(Token),
+ BinOp::ShlAssign(Token),
+ ] {
+ f(Expr::Binary(ExprBinary {
+ // `x + $expr`
+ attrs: Vec::new(),
+ left: Box::new(simple.clone()),
+ op,
+ right: Box::new(expr.clone()),
+ }));
+ f(Expr::Binary(ExprBinary {
+ // `$expr + x`
+ attrs: Vec::new(),
+ left: Box::new(expr.clone()),
+ op,
+ right: Box::new(simple.clone()),
+ }));
+ }
+ });
+ });
+
+ // Expr::Block
+ f(Expr::Block(ExprBlock {
+ // `{}`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Break
+ f(Expr::Break(ExprBreak {
+ // `break`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: None,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Break(ExprBreak {
+ // `break $expr`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: None,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Call
+ iter(depth, &mut |expr| {
+ f(Expr::Call(ExprCall {
+ // `$expr()`
+ attrs: Vec::new(),
+ func: Box::new(expr),
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ });
+
+ // Expr::Cast
+ iter(depth, &mut |expr| {
+ f(Expr::Cast(ExprCast {
+ // `$expr as T`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ as_token: Token,
+ ty: Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ }));
+ });
+
+ // Expr::Closure
+ iter(depth, &mut |expr| {
+ f(Expr::Closure(ExprClosure {
+ // `|| $expr`
+ attrs: Vec::new(),
+ lifetimes: None,
+ constness: None,
+ movability: None,
+ asyncness: None,
+ capture: None,
+ or1_token: Token,
+ inputs: Punctuated::new(),
+ or2_token: Token,
+ output: ReturnType::Default,
+ body: Box::new(expr),
+ }));
+ });
+
+ // Expr::Field
+ iter(depth, &mut |expr| {
+ f(Expr::Field(ExprField {
+ // `$expr.field`
+ attrs: Vec::new(),
+ base: Box::new(expr),
+ dot_token: Token,
+ member: Member::Named(Ident::new("field", span)),
+ }));
+ });
+
+ // Expr::If
+ iter(depth, &mut |expr| {
+ f(Expr::If(ExprIf {
+ // `if $expr {}`
+ attrs: Vec::new(),
+ if_token: Token,
+ cond: Box::new(expr),
+ then_branch: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ else_branch: None,
+ }));
+ });
+
+ // Expr::Let
+ iter(depth, &mut |expr| {
+ f(Expr::Let(ExprLet {
+ attrs: Vec::new(),
+ let_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ eq_token: Token,
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Range
+ f(Expr::Range(ExprRange {
+ // `..`
+ attrs: Vec::new(),
+ start: None,
+ limits: RangeLimits::HalfOpen(Token),
+ end: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Range(ExprRange {
+ // `..$expr`
+ attrs: Vec::new(),
+ start: None,
+ limits: RangeLimits::HalfOpen(Token),
+ end: Some(Box::new(expr.clone())),
+ }));
+ f(Expr::Range(ExprRange {
+ // `$expr..`
+ attrs: Vec::new(),
+ start: Some(Box::new(expr)),
+ limits: RangeLimits::HalfOpen(Token),
+ end: None,
+ }));
+ });
+
+ // Expr::Reference
+ iter(depth, &mut |expr| {
+ f(Expr::Reference(ExprReference {
+ // `&$expr`
+ attrs: Vec::new(),
+ and_token: Token,
+ mutability: None,
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Return
+ f(Expr::Return(ExprReturn {
+ // `return`
+ attrs: Vec::new(),
+ return_token: Token,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Return(ExprReturn {
+ // `return $expr`
+ attrs: Vec::new(),
+ return_token: Token,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Try
+ iter(depth, &mut |expr| {
+ f(Expr::Try(ExprTry {
+ // `$expr?`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ question_token: Token,
+ }));
+ });
+
+ // Expr::Unary
+ iter(depth, &mut |expr| {
+ for op in [
+ UnOp::Deref(Token),
+ //UnOp::Not(Token),
+ //UnOp::Neg(Token),
+ ] {
+ f(Expr::Unary(ExprUnary {
+ // `*$expr`
+ attrs: Vec::new(),
+ op,
+ expr: Box::new(expr.clone()),
+ }));
+ }
+ });
+
+ if false {
+ // Expr::Array
+ f(Expr::Array(ExprArray {
+ // `[]`
+ attrs: Vec::new(),
+ bracket_token: token::Bracket(span),
+ elems: Punctuated::new(),
+ }));
+
+ // Expr::Async
+ f(Expr::Async(ExprAsync {
+ // `async {}`
+ attrs: Vec::new(),
+ async_token: Token,
+ capture: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Await
+ iter(depth, &mut |expr| {
+ f(Expr::Await(ExprAwait {
+ // `$expr.await`
+ attrs: Vec::new(),
+ base: Box::new(expr),
+ dot_token: Token,
+ await_token: Token,
+ }));
+ });
+
+ // Expr::Block
+ f(Expr::Block(ExprBlock {
+ // `'a: {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Block(ExprBlock {
+ // `{ $expr }`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::from([Stmt::Expr(expr.clone(), None)]),
+ },
+ }));
+ f(Expr::Block(ExprBlock {
+ // `{ $expr; }`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::from([Stmt::Expr(expr, Some(Token))]),
+ },
+ }));
+ });
+
+ // Expr::Break
+ f(Expr::Break(ExprBreak {
+ // `break 'a`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Break(ExprBreak {
+ // `break 'a $expr`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Closure
+ f(Expr::Closure(ExprClosure {
+ // `|| -> T {}`
+ attrs: Vec::new(),
+ lifetimes: None,
+ constness: None,
+ movability: None,
+ asyncness: None,
+ capture: None,
+ or1_token: Token,
+ inputs: Punctuated::new(),
+ or2_token: Token,
+ output: ReturnType::Type(
+ Token,
+ Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ ),
+ body: Box::new(Expr::Block(ExprBlock {
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ })),
+ }));
+
+ // Expr::Const
+ f(Expr::Const(ExprConst {
+ // `const {}`
+ attrs: Vec::new(),
+ const_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Continue
+ f(Expr::Continue(ExprContinue {
+ // `continue`
+ attrs: Vec::new(),
+ continue_token: Token,
+ label: None,
+ }));
+ f(Expr::Continue(ExprContinue {
+ // `continue 'a`
+ attrs: Vec::new(),
+ continue_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ }));
+
+ // Expr::ForLoop
+ iter(depth, &mut |expr| {
+ f(Expr::ForLoop(ExprForLoop {
+ // `for _ in $expr {}`
+ attrs: Vec::new(),
+ label: None,
+ for_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ in_token: Token,
+ expr: Box::new(expr.clone()),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::ForLoop(ExprForLoop {
+ // `'a: for _ in $expr {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ for_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ in_token: Token,
+ expr: Box::new(expr),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ });
+
+ // Expr::Index
+ iter(depth, &mut |expr| {
+ f(Expr::Index(ExprIndex {
+ // `$expr[0]`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ bracket_token: token::Bracket(span),
+ index: Box::new(Expr::Lit(ExprLit {
+ attrs: Vec::new(),
+ lit: Lit::Int(LitInt::new("0", span)),
+ })),
+ }));
+ });
+
+ // Expr::Loop
+ f(Expr::Loop(ExprLoop {
+ // `loop {}`
+ attrs: Vec::new(),
+ label: None,
+ loop_token: Token,
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::Loop(ExprLoop {
+ // `'a: loop {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ loop_token: Token,
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Macro
+ f(Expr::Macro(ExprMacro {
+ // `m!()`
+ attrs: Vec::new(),
+ mac: Macro {
+ path: Path::from(Ident::new("m", span)),
+ bang_token: Token,
+ delimiter: MacroDelimiter::Paren(token::Paren(span)),
+ tokens: TokenStream::new(),
+ },
+ }));
+ f(Expr::Macro(ExprMacro {
+ // `m! {}`
+ attrs: Vec::new(),
+ mac: Macro {
+ path: Path::from(Ident::new("m", span)),
+ bang_token: Token,
+ delimiter: MacroDelimiter::Brace(token::Brace(span)),
+ tokens: TokenStream::new(),
+ },
+ }));
+
+ // Expr::Match
+ iter(depth, &mut |expr| {
+ f(Expr::Match(ExprMatch {
+ // `match $expr {}`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(expr.clone()),
+ brace_token: token::Brace(span),
+ arms: Vec::new(),
+ }));
+ f(Expr::Match(ExprMatch {
+ // `match x { _ => $expr }`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ })),
+ brace_token: token::Brace(span),
+ arms: Vec::from([Arm {
+ attrs: Vec::new(),
+ pat: Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ }),
+ guard: None,
+ fat_arrow_token: Token,
+ body: Box::new(expr.clone()),
+ comma: None,
+ }]),
+ }));
+ f(Expr::Match(ExprMatch {
+ // `match x { _ if $expr => {} }`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ })),
+ brace_token: token::Brace(span),
+ arms: Vec::from([Arm {
+ attrs: Vec::new(),
+ pat: Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ }),
+ guard: Some((Token, Box::new(expr))),
+ fat_arrow_token: Token,
+ body: Box::new(Expr::Block(ExprBlock {
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ })),
+ comma: None,
+ }]),
+ }));
+ });
+
+ // Expr::MethodCall
+ iter(depth, &mut |expr| {
+ f(Expr::MethodCall(ExprMethodCall {
+ // `$expr.method()`
+ attrs: Vec::new(),
+ receiver: Box::new(expr.clone()),
+ dot_token: Token,
+ method: Ident::new("method", span),
+ turbofish: None,
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ f(Expr::MethodCall(ExprMethodCall {
+ // `$expr.method::<T>()`
+ attrs: Vec::new(),
+ receiver: Box::new(expr),
+ dot_token: Token,
+ method: Ident::new("method", span),
+ turbofish: Some(AngleBracketedGenericArguments {
+ colon2_token: Some(Token),
+ lt_token: Token,
+ args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
+ TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ },
+ ))]),
+ gt_token: Token,
+ }),
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ });
+
+ // Expr::RawAddr
+ iter(depth, &mut |expr| {
+ f(Expr::RawAddr(ExprRawAddr {
+ // `&raw const $expr`
+ attrs: Vec::new(),
+ and_token: Token,
+ raw: Token,
+ mutability: PointerMutability::Const(Token),
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Struct
+ f(Expr::Struct(ExprStruct {
+ // `Struct {}`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("Struct", span)),
+ brace_token: token::Brace(span),
+ fields: Punctuated::new(),
+ dot2_token: None,
+ rest: None,
+ }));
+
+ // Expr::TryBlock
+ f(Expr::TryBlock(ExprTryBlock {
+ // `try {}`
+ attrs: Vec::new(),
+ try_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Unsafe
+ f(Expr::Unsafe(ExprUnsafe {
+ // `unsafe {}`
+ attrs: Vec::new(),
+ unsafe_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::While
+ iter(depth, &mut |expr| {
+ f(Expr::While(ExprWhile {
+ // `while $expr {}`
+ attrs: Vec::new(),
+ label: None,
+ while_token: Token,
+ cond: Box::new(expr.clone()),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::While(ExprWhile {
+ // `'a: while $expr {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ while_token: Token,
+ cond: Box::new(expr),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ });
+
+ // Expr::Yield
+ f(Expr::Yield(ExprYield {
+ // `yield`
+ attrs: Vec::new(),
+ yield_token: Token,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Yield(ExprYield {
+ // `yield $expr`
+ attrs: Vec::new(),
+ yield_token: Token,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+ }
+ }
+
+ let mut failures = 0;
+ macro_rules! fail {
+ ($($message:tt)*) => {{
+ eprintln!($($message)*);
+ failures += 1;
+ return;
+ }};
+ }
+ let mut assert = |mut original: Expr| {
+ let span = Span::call_site();
+ // `const _: () = $expr;`
+ let pretty = prettyplease::unparse(&File {
+ shebang: None,
+ attrs: Vec::new(),
+ items: Vec::from([Item::Const(ItemConst {
+ attrs: Vec::new(),
+ vis: Visibility::Inherited,
+ const_token: Token,
+ ident: Ident::from(Token),
+ generics: Generics::default(),
+ colon_token: Token,
+ ty: Box::new(Type::Infer(TypeInfer {
+ underscore_token: Token,
+ })),
+ eq_token: Token,
+ expr: Box::new(original.clone()),
+ semi_token: Token,
+ })]),
+ });
+ let mut parsed = match syn::parse_file(&pretty) {
+ Ok(parsed) => parsed,
+ _ => fail!("failed to parse: {pretty}{original:#?}"),
+ };
+ let item = match parsed.items.as_mut_slice() {
+ [Item::Const(item)] => item,
+ _ => unreachable!(),
+ };
+ let mut parsed = mem::replace(&mut *item.expr, Expr::PLACEHOLDER);
+ AsIfPrinted.visit_expr_mut(&mut original);
+ FlattenParens.visit_expr_mut(&mut parsed);
+ if original != parsed {
+ fail!(
+ "before: {}\n{:#?}\nafter: {}\n{:#?}",
+ original.to_token_stream(),
+ original,
+ parsed.to_token_stream(),
+ parsed,
+ );
+ }
+ if pretty.contains("(||") {
+ // https://github.com/dtolnay/prettyplease/issues/99
+ return;
+ }
+ let no_paren = pretty.replace(['(', ')'], "");
+ if pretty != no_paren {
+ if let Ok(mut parsed2) = syn::parse_file(&no_paren) {
+ let item = match parsed2.items.as_mut_slice() {
+ [Item::Const(item)] => item,
+ _ => unreachable!(),
+ };
+ if original == *item.expr {
+ fail!("redundant parens: {}", pretty);
+ }
+ }
+ }
+ };
+
+ iter(if cfg!(debug_assertions) { 3 } else { 4 }, &mut assert);
+ if failures > 0 {
+ eprintln!("FAILURES: {failures}");
+ ExitCode::FAILURE
+ } else {
+ ExitCode::SUCCESS
+ }
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 984e54a..e9cf7c7 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -338,7 +338,7 @@
"predicates",
"predicates-core",
"predicates-tree",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"protobuf 3.2.0",
"protobuf-codegen",
@@ -890,7 +890,7 @@
"lazy_static",
"lazycell",
"log",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
@@ -3001,20 +3001,11 @@
[[package]]
name = "litrs"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b487d13a3f4b465df87895a37b24e364907019afa12d943528df5b7abe0836f1"
-dependencies = [
- "proc-macro2 1.0.93",
-]
-
-[[package]]
-name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
dependencies = [
- "proc-macro2 1.0.92",
+ "proc-macro2 1.0.93",
]
[[package]]
@@ -3634,7 +3625,7 @@
dependencies = [
"proc-macro2 1.0.93",
"quote 1.0.38",
- "syn 2.0.90",
+ "syn 2.0.96",
]
[[package]]
@@ -3784,7 +3775,7 @@
"heck",
"pest",
"pest_derive",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"quote 1.0.38",
"serde",
@@ -4084,9 +4075,9 @@
[[package]]
name = "prettyplease"
-version = "0.2.25"
+version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
dependencies = [
"proc-macro2 1.0.93",
"syn 2.0.96",
@@ -4371,7 +4362,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
]
[[package]]
@@ -5229,9 +5220,9 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
dependencies = [
- "proc-macro2 1.0.92",
+ "proc-macro2 1.0.93",
"quote 1.0.38",
- "syn 2.0.90",
+ "syn 2.0.96",
]
[[package]]
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index e24e8f0..625543b 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -253,7 +253,7 @@
predicates = "=3.1.3"
predicates-core = "=1.0.9"
predicates-tree = "=1.0.11"
-prettyplease = "=0.2.25"
+prettyplease = "=0.2.29"
proc-macro2 = "=1.0.93"
protobuf = "=3.2.0"
protobuf-codegen = "=3.2.0"