Merge remote-tracking branch 'origin/master' into difflines_mode

This commit is contained in:
Chris Emerson 2018-02-12 22:43:37 +00:00
commit c2fda14e0a
609 changed files with 2778 additions and 1768 deletions

View File

@ -2,6 +2,11 @@
## [Unreleased]
### Added
- Add `use_field_init_shorthand` config option.
- Add `reorder_modules` configuration option.
## [0.3.6] 2018-01-18
### Fixed

153
Cargo.lock generated
View File

@ -32,6 +32,15 @@ name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cargo-fmt"
version = "0.4.0"
dependencies = [
"cargo_metadata 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cargo_metadata"
version = "0.4.1"
@ -106,9 +115,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.2.15"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "git-rustfmt"
version = "0.4.0"
dependencies = [
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-config 0.4.0",
"rustfmt-core 0.4.0",
]
[[package]]
name = "itoa"
version = "0.3.4"
@ -159,7 +179,15 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.1.41"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -185,7 +213,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -197,11 +225,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.20"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -223,7 +262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-ap-rustc_cratesio_shim"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -232,57 +271,57 @@ dependencies = [
[[package]]
name = "rustc-ap-rustc_data_structures"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-ap-rustc_errors"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-ap-rustc_data_structures 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax_pos 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_data_structures 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax_pos 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-ap-serialize"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-ap-syntax"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_cratesio_shim 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_data_structures 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_errors 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax_pos 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_cratesio_shim 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_data_structures 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_errors 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax_pos 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-ap-syntax_pos"
version = "12.0.0"
version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-ap-rustc_data_structures 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_data_structures 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -293,28 +332,56 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustfmt-nightly"
version = "0.3.6"
name = "rustfmt-bin"
version = "0.4.0"
dependencies = [
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-config 0.4.0",
"rustfmt-core 0.4.0",
]
[[package]]
name = "rustfmt-config"
version = "0.4.0"
dependencies = [
"rustc-ap-syntax 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustfmt-core"
version = "0.4.0"
dependencies = [
"cargo_metadata 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"derive-new 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_errors 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_errors 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-syntax 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-config 0.4.0",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustfmt-format-diff"
version = "0.4.0"
dependencies = [
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -362,7 +429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -497,7 +564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
"checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
@ -505,20 +572,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum rustc-ap-rustc_cratesio_shim 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1a51c10af5abd5d698b7e3487e869e6d15f6feb04cbedb5c792e2824f9d845e"
"checksum rustc-ap-rustc_data_structures 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1aa227490501072780d57f74b1164d361833ff8e172f817da0da2cdf2e4280cc"
"checksum rustc-ap-rustc_errors 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21ff6c6e13ac4fc04b7d4d398828b024c4b6577045cb3175b33d35fea35ff6d0"
"checksum rustc-ap-serialize 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b4e7f51e298675c2bf830f7265621a8936fb09e63b825b58144cbaac969e604"
"checksum rustc-ap-syntax 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8bf5639869ba2f7fa581939cd217cb71a85506b82ad0ea520614fb0dceb2386c"
"checksum rustc-ap-syntax_pos 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c020cdb7379e1c733ae0a311ae47c748337ba584d2dd7b7f53baaae78de6f8b"
"checksum rustc-ap-rustc_cratesio_shim 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ad5e562044ea78a6764dd75ae8afe4b21fde49f4548024b5fdf6345c21fb524"
"checksum rustc-ap-rustc_data_structures 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c0d65325492aba7db72899e3edbab34d39af98c42ab7c7e450c9a288ffe4ad"
"checksum rustc-ap-rustc_errors 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87d4ab2e06a671b5b5c5b0359dac346f164c99d059dce6a22feb08f2f56bd182"
"checksum rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0745fa445ff41c4b6699936cf35ce3ca49502377dd7b3929c829594772c3a7b"
"checksum rustc-ap-syntax 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82efedabe30f393161e11214a9130edfa01ad476372d1c6f3fec1f8d30488c9d"
"checksum rustc-ap-syntax_pos 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9de2e927e280c75b8efab9c5f591ad31082d5d2c4c562c68fdba2ee77286b0"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"

View File

@ -1,58 +1,9 @@
[package]
name = "rustfmt-nightly"
version = "0.3.6"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
build = "build.rs"
categories = ["development-tools"]
[lib]
doctest = false
[[bin]]
name = "rustfmt"
[[bin]]
name = "cargo-fmt"
[[bin]]
name = "rustfmt-format-diff"
[[bin]]
name = "git-rustfmt"
[features]
default = ["cargo-fmt", "rustfmt-format-diff"]
cargo-fmt = []
rustfmt-format-diff = []
[dependencies]
toml = "0.4"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
unicode-segmentation = "1.0.0"
regex = "0.2"
term = "0.4"
diff = "0.1"
log = "0.3"
env_logger = "0.4"
getopts = "0.2"
derive-new = "0.5"
cargo_metadata = "0.4"
rustc-ap-syntax = "12.0.0"
rustc-ap-rustc_errors = "12.0.0"
[dev-dependencies]
lazy_static = "1.0.0"
[target.'cfg(unix)'.dependencies]
libc = "0.2.11"
[target.'cfg(windows)'.dependencies]
kernel32-sys = "0.2.2"
winapi = "0.2.7"
[workspace]
members = [
"cargo-fmt",
"git-rustfmt",
"rustfmt-bin",
"rustfmt-config",
"rustfmt-core",
"rustfmt-format-diff",
]

View File

@ -31,27 +31,31 @@ Indent on expressions or items.
#### `"Block"` (default):
```rust
let lorem = vec![
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
];
fn main() {
let lorem = vec![
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
];
}
```
#### `"Visual"`:
```rust
let lorem = vec!["ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit"];
fn main() {
let lorem = vec!["ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit"];
}
```
### Control flow
@ -59,21 +63,24 @@ let lorem = vec!["ipsum",
#### `"Block"` (default):
```rust
if lorem_ipsum &&
dolor_sit &&
amet_consectetur
{
// ...
fn main() {
if lorem_ipsum && dolor_sit && amet_consectetur && lorem_sit && dolor_consectetur && amet_ipsum
&& lorem_consectetur
{
// ...
}
}
```
#### `"Visual"`:
```rust
if lorem_ipsum &&
dolor_sit &&
amet_consectetur {
// ...
fn main() {
if lorem_ipsum && dolor_sit && amet_consectetur && lorem_sit && dolor_consectetur && amet_ipsum
&& lorem_consectetur
{
// ...
}
}
```
@ -124,29 +131,33 @@ fn lorem(ipsum: usize,
#### `"Block"` (default):
```rust
lorem(
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
);
fn main() {
lorem(
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
);
}
```
#### `"Visual"`:
```rust
lorem("lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit");
fn main() {
lorem("lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit");
}
```
### Generics
@ -161,7 +172,7 @@ fn lorem<
Amet: Eq = usize,
Adipiscing: Eq = usize,
Consectetur: Eq = usize,
Elit: Eq = usize
Elit: Eq = usize,
>(
ipsum: Ipsum,
dolor: Dolor,
@ -184,15 +195,15 @@ fn lorem<Ipsum: Eq = usize,
Amet: Eq = usize,
Adipiscing: Eq = usize,
Consectetur: Eq = usize,
Elit: Eq = usize>
(ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
adipiscing: Adipiscing,
consectetur: Consectetur,
elit: Elit)
-> T {
Elit: Eq = usize>(
ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
adipiscing: Adipiscing,
consectetur: Consectetur,
elit: Elit)
-> T {
// body
}
```
@ -202,17 +213,21 @@ fn lorem<Ipsum: Eq = usize,
#### `"Block"` (default):
```rust
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
fn main() {
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
}
```
#### `"Visual"`:
```rust
let lorem = Lorem { ipsum: dolor,
sit: amet, };
fn main() {
let lorem = Lorem { ipsum: dolor,
sit: amet, };
}
```
See also: [`struct_lit_single_line`](#struct_lit_single_line), [`indent_style`](#indent_style).
@ -227,7 +242,7 @@ where
Ipsum: Eq,
Dolor: Eq,
Sit: Eq,
Amet: Eq
Amet: Eq,
{
// body
}
@ -274,7 +289,11 @@ fn main() {
"adipiscing",
);
let lorem = Lorem { ipsum: dolor, sit: amet };
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
let lorem = Lorem { ipsum: dolor };
let lorem = if ipsum { dolor } else { sit };
}
@ -319,16 +338,16 @@ Where to put a binary operator when a binary expression goes multiline.
#### `"Front"` (default):
```rust
let or = foo
|| bar
|| foobar;
fn main() {
let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
|| barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar;
let sum = 1234
+ 5678
+ 910;
let sum = 123456789012345678901234567890 + 123456789012345678901234567890
+ 123456789012345678901234567890;
let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
..bbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
}
```
#### `"Back"`:
@ -488,13 +507,18 @@ Replace strings of _ wildcards by a single .. in tuple patterns
#### `false` (default):
```rust
let (lorem, ipsum, _, _) = (1, 2, 3, 4);
fn main() {
let (lorem, ipsum, _, _) = (1, 2, 3, 4);
let (lorem, ipsum, ..) = (1, 2, 3, 4);
}
```
#### `true`:
```rust
let (lorem, ipsum, ..) = (1, 2, 3, 4);
fn main() {
let (lorem, ipsum, ..) = (1, 2, 3, 4);
}
```
## `control_brace_style`
@ -508,34 +532,40 @@ Brace style for control flow constructs
#### `"AlwaysSameLine"` (default):
```rust
if lorem {
println!("ipsum!");
} else {
println!("dolor!");
fn main() {
if lorem {
println!("ipsum!");
} else {
println!("dolor!");
}
}
```
#### `"AlwaysNextLine"`:
```rust
if lorem
{
println!("ipsum!");
}
else
{
println!("dolor!");
fn main() {
if lorem
{
println!("ipsum!");
}
else
{
println!("dolor!");
}
}
```
#### `"ClosingNextLine"`:
```rust
if lorem {
println!("ipsum!");
}
else {
println!("dolor!");
fn main() {
if lorem {
println!("ipsum!");
}
else {
println!("dolor!");
}
}
```
@ -637,33 +667,41 @@ trait Lorem {
```rust
trait Lorem {
fn lorem(ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet);
fn lorem(
ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
);
fn lorem(ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet) {
fn lorem(
ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
) {
// body
}
fn lorem(ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
consectetur: Consectetur,
adipiscing: Adipiscing,
elit: Elit);
fn lorem(
ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
consectetur: Consectetur,
adipiscing: Adipiscing,
elit: Elit,
);
fn lorem(ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
consectetur: Consectetur,
adipiscing: Adipiscing,
elit: Elit) {
fn lorem(
ipsum: Ipsum,
dolor: Dolor,
sit: Sit,
amet: Amet,
consectetur: Consectetur,
adipiscing: Adipiscing,
elit: Elit,
) {
// body
}
}
@ -748,7 +786,8 @@ struct Lorem {
}
struct Dolor<T>
where T: Eq
where
T: Eq,
{
sit: T,
}
@ -763,7 +802,8 @@ struct Lorem
}
struct Dolor<T>
where T: Eq
where
T: Eq,
{
sit: T,
}
@ -777,7 +817,8 @@ struct Lorem {
}
struct Dolor<T>
where T: Eq {
where
T: Eq, {
sit: T,
}
```
@ -862,7 +903,7 @@ impl<T> Lorem for T
where
Option<T>: Ipsum,
{
...
// body
}
```
@ -870,8 +911,9 @@ where
```rust
impl<T> Lorem for T
where Option<T>: Ipsum {
...
where Option<T>: Ipsum
{
// body
}
```
@ -915,15 +957,19 @@ Format string literals where necessary
#### `false` (default):
```rust
let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit";
fn main() {
let lorem =
"ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet consectetur adipiscing";
}
```
#### `true`:
```rust
let lorem =
"ipsum dolor sit amet consectetur \
adipiscing elit lorem ipsum dolor sit";
fn main() {
let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet \
consectetur adipiscing";
}
```
See also [`max_width`](#max_width).
@ -966,18 +1012,16 @@ Indent style of imports
#### `"Visual"` (default):
```rust
use foo::{xxx,
yyy,
zzz};
use foo::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz};
```
#### `"Block"`:
```rust
use foo::{
xxx,
yyy,
zzz,
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
};
```
@ -994,10 +1038,10 @@ Item layout inside a imports block
#### `"Mixed"` (default):
```rust
use foo::{xxx, yyy, zzz};
use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz};
use foo::{aaa, bbb, ccc,
ddd, eee, fff};
use foo::{aaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd,
eeeeeeeeeeeeeeeeee, ffffffffffffffffff};
```
#### `"Horizontal"`:
@ -1013,14 +1057,14 @@ use foo::{aaa, bbb, ccc, ddd, eee, fff};
#### `"HorizontalVertical"`:
```rust
use foo::{xxx, yyy, zzz};
use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz};
use foo::{aaa,
bbb,
ccc,
ddd,
eee,
fff};
use foo::{aaaaaaaaaaaaaaaaaa,
bbbbbbbbbbbbbbbbbb,
cccccccccccccccccc,
dddddddddddddddddd,
eeeeeeeeeeeeeeeeee,
ffffffffffffffffff};
```
#### `"Vertical"`:
@ -1050,22 +1094,26 @@ Put a trailing comma after a block based match arm (non-block arms are not affec
#### `false` (default):
```rust
match lorem {
Lorem::Ipsum => {
println!("ipsum");
fn main() {
match lorem {
Lorem::Ipsum => {
println!("ipsum");
}
Lorem::Dolor => println!("dolor"),
}
Lorem::Dolor => println!("dolor"),
}
```
#### `true`:
```rust
match lorem {
Lorem::Ipsum => {
println!("ipsum");
},
Lorem::Dolor => println!("dolor"),
fn main() {
match lorem {
Lorem::Ipsum => {
println!("ipsum");
},
Lorem::Dolor => println!("dolor"),
}
}
```
@ -1116,37 +1164,40 @@ Force multiline closure and match arm bodies to be wrapped in a block
#### `false` (default):
```rust
result.and_then(|maybe_value| match maybe_value {
None => ...,
Some(value) => ...,
})
fn main() {
result.and_then(|maybe_value| match maybe_value {
None => foo(),
Some(value) => bar(),
});
match lorem {
None => if ipsum {
println!("Hello World");
},
Some(dolor) => ...,
match lorem {
None => if ipsum {
println!("Hello World");
},
Some(dolor) => foo(),
}
}
```
#### `true`:
```rust
result.and_then(|maybe_value| {
match maybe_value {
None => ...,
Some(value) => ...,
}
})
match lorem {
None => {
if ipsum {
println!("Hello World");
fn main() {
result.and_then(|maybe_value| {
match maybe_value {
None => foo(),
Some(value) => bar(),
}
});
match lorem {
None => {
if ipsum {
println!("Hello World");
}
}
Some(dolor) => foo(),
}
Some(dolor) => ...,
}
```
@ -1468,7 +1519,7 @@ struct Foo {
## `spaces_around_ranges`
Put spaces around the .. and ... range operators
Put spaces around the .., ..=, and ... range operators
- **Default value**: `false`
- **Possible values**: `true`, `false`
@ -1477,13 +1528,49 @@ Put spaces around the .. and ... range operators
#### `false` (default):
```rust
let lorem = 0..10;
fn main() {
let lorem = 0..10;
let ipsum = 0..=10;
match lorem {
1..5 => foo(),
_ => bar,
}
match lorem {
1..=5 => foo(),
_ => bar,
}
match lorem {
1...5 => foo(),
_ => bar,
}
}
```
#### `true`:
```rust
let lorem = 0 .. 10;
fn main() {
let lorem = 0 .. 10;
let ipsum = 0 ..= 10;
match lorem {
1 .. 5 => foo(),
_ => bar,
}
match lorem {
1 ..= 5 => foo(),
_ => bar,
}
match lorem {
1 ... 5 => foo(),
_ => bar,
}
}
```
## `spaces_within_parens_and_brackets`
@ -1508,24 +1595,28 @@ fn lorem<T: Eq>(t: T) {
}
// non-empty square brackets
let lorem: [usize; 2] = [ipsum, dolor];
fn lorem<T: Eq>(t: T) {
let lorem: [usize; 2] = [ipsum, dolor];
}
```
#### `true`:
```rust
// generic arguments
fn lorem< T: Eq >(t: T) {
fn lorem< T: Eq >( t: T ) {
// body
}
// non-empty parentheses
fn lorem<T: Eq>( t: T ) {
fn lorem< T: Eq >( t: T ) {
let lorem = ( ipsum, dolor );
}
// non-empty square brackets
let lorem: [ usize; 2 ] = [ ipsum, dolor ];
fn lorem< T: Eq >( t: T ) {
let lorem: [ usize; 2 ] = [ ipsum, dolor ];
}
```
## `struct_lit_single_line`
@ -1545,10 +1636,12 @@ let lorem = Lorem { ipsum: dolor, sit: amet };
#### `false`:
```rust
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
fn main() {
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
}
```
See also: [`indent_style`](#indent_style).
@ -1568,7 +1661,7 @@ Number of spaces per tab
fn lorem() {
let ipsum = dolor();
let sit = vec![
"amet consectetur adipiscing elit."
"amet consectetur adipiscing elit amet consectetur adipiscing elit amet consectetur.",
];
}
```
@ -1579,7 +1672,7 @@ fn lorem() {
fn lorem() {
let ipsum = dolor();
let sit = vec![
"amet consectetur adipiscing elit."
"amet consectetur adipiscing elit amet consectetur adipiscing elit amet consectetur.",
];
}
```
@ -1598,43 +1691,49 @@ How to handle trailing commas for lists
#### `"Vertical"` (default):
```rust
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
fn main() {
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
}
```
#### `"Always"`:
```rust
let Lorem { ipsum, dolor, sit, } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
fn main() {
let Lorem { ipsum, dolor, sit, } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
}
```
#### `"Never"`:
```rust
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing
} = elit;
fn main() {
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing
} = elit;
}
```
See also: [`match_block_trailing_comma`](#match_block_trailing_comma).
@ -1673,7 +1772,7 @@ Determines if `+` or `=` are wrapped in spaces in the punctuation of types
```rust
fn lorem<Ipsum: Dolor + Sit = Amet>() {
// body
// body
}
```
@ -1681,7 +1780,49 @@ fn lorem<Ipsum: Dolor + Sit = Amet>() {
```rust
fn lorem<Ipsum: Dolor+Sit=Amet>() {
// body
// body
}
```
## `use_field_init_shorthand`
Use field initialize shorthand if possible.
- **Default value**: `false`
- **Possible values**: `true`, `false`
- **Stable**: No
#### `false` (default):
```rust
struct Foo {
x: u32,
y: u32,
z: u32,
}
fn main() {
let x = 1;
let y = 2;
let z = 3;
let a = Foo { x: x, y: y, z: z };
}
```
#### `true`:
```rust
struct Foo {
x: u32,
y: u32,
z: u32,
}
fn main() {
let x = 1;
let y = 2;
let z = 3;
let a = Foo { x, y, z };
}
```
@ -1696,13 +1837,17 @@ Replace uses of the try! macro by the ? shorthand
#### `false` (default):
```rust
let lorem = try!(ipsum.map(|dolor|dolor.sit()));
fn main() {
let lorem = try!(ipsum.map(|dolor| dolor.sit()));
}
```
#### `true`:
```rust
let lorem = ipsum.map(|dolor| dolor.sit())?;
fn main() {
let lorem = ipsum.map(|dolor| dolor.sit())?;
}
```
@ -1741,21 +1886,25 @@ Wrap the body of arms in blocks when it does not fit on the same line with the p
#### `true` (default):
```rust
match lorem {
true => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
fn main() {
match lorem {
true => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
}
false => println!("{}", sit),
}
false => println!("{}", sit),
}
```
#### `false`:
```rust
match lorem {
true =>
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
false => println!("{}", sit),
fn main() {
match lorem {
true =>
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
false => println!("{}", sit),
}
}
```
@ -1782,6 +1931,8 @@ lines are found, they are trimmed down to match this integer.
Original Code:
```rust
#![rustfmt_skip]
fn foo() {
println!("a");
}
@ -1839,6 +1990,8 @@ them, additional blank lines are inserted.
Original Code (rustfmt will not change it with the default value of `0`):
```rust
#![rustfmt_skip]
fn foo() {
println!("a");
}

View File

@ -21,31 +21,29 @@ to be a bit out of date). Version 0.1 of rustfmt-nightly is forked from version
## Quick start
You must be using the latest nightly compiler toolchain.
Currently, you can use `rustfmt` on nightly and beta. Rust 1.24 stable will work,
but we're not quite there yet!
To install:
```
cargo install rustfmt-nightly
rustup component add rustfmt-preview --toolchain=nightly
```
If `nightly` is your default toolchain, you can leave the `--toolchain` off.
to run on a cargo project in the current working directory:
```
cargo fmt
cargo +nightly fmt
```
If `nightly` is your default toolchain, you can leave off the `+nightly`.
## Installation
```
cargo install rustfmt-nightly
```
or if you're using [Rustup](https://www.rustup.rs/)
```
rustup update
rustup run nightly cargo install rustfmt-nightly
rustup component add rustfmt-preview --toolchain=nightly
```
If you don't have a nightly toolchain, you can add it using rustup:
@ -63,12 +61,6 @@ rustup default nightly
If you choose not to do that you'll have to run rustfmt using `rustup run ...`
or by adding `+nightly` to the cargo invocation.
Usually cargo-fmt, which enables usage of Cargo subcommand `cargo fmt`, is
installed alongside rustfmt. To only install rustfmt run
```
cargo install --no-default-features rustfmt-nightly
```
## Installing from source
To install from source, first checkout to the tag or branch you want to install, then issue
@ -151,25 +143,20 @@ when a pull request contains unformatted code. Using `--write-mode=diff` instruc
rustfmt to exit with an error code if the input is not formatted correctly.
It will also print any found differences.
(These instructions use the nightly version of Rustfmt. If you want to use the
Syntex version replace `install rustfmt-nightly` with `install rustfmt`).
A minimal Travis setup could look like this:
```yaml
language: rust
cache: cargo
before_script:
- export PATH="$PATH:$HOME/.cargo/bin"
- which rustfmt || cargo install rustfmt-nightly
- rustup toolchain install nightly
- rustup component add --toolchain nightly rustfmt-preview
- which rustfmt || cargo install --force rustfmt-nightly
script:
- cargo fmt -- --write-mode=diff
- cargo +nightly fmt --all -- --write-mode=diff
- cargo build
- cargo test
```
Note that using `cache: cargo` is optional but highly recommended to speed up the installation.
## How to build and test
`cargo build` to build.

View File

@ -47,9 +47,6 @@ install:
# ???
build: false
# Build rustfmt, run the executables as
test_script:
- cargo build --verbose
- cargo run --bin rustfmt -- --help
- cargo run --bin cargo-fmt -- --help
- cargo test

17
cargo-fmt/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "cargo-fmt"
version = "0.4.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "Cargo frontend for rustfmt"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
categories = ["development-tools"]
[[bin]]
name = "cargo-fmt"
[dependencies]
cargo_metadata = "0.4"
getopts = "0.2"
serde_json = "1.0"

View File

@ -97,14 +97,14 @@ fn execute() -> i32 {
}
macro_rules! print_usage {
($print:ident, $opts:ident, $reason:expr) => ({
($print: ident, $opts: ident, $reason: expr) => {{
let msg = format!("{}\nusage: cargo fmt [options]", $reason);
$print!(
"{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
Arguments after `--` are passed to rustfmt.",
$opts.usage(&msg)
);
})
}};
}
fn print_usage_to_stdout(opts: &Options, reason: &str) {

19
git-rustfmt/Cargo.toml Normal file
View File

@ -0,0 +1,19 @@
[package]
name = "git-rustfmt"
version = "0.4.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "Run rustfmt against git diff"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
categories = ["development-tools"]
[[bin]]
name = "git-rustfmt"
[dependencies]
env_logger = "0.4"
getopts = "0.2"
log = "0.3"
rustfmt-config = { path = "../rustfmt-config" }
rustfmt-core = { path = "../rustfmt-core" }

View File

@ -1,8 +1,19 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate env_logger;
extern crate getopts;
#[macro_use]
extern crate log;
extern crate rustfmt_nightly as rustfmt;
extern crate rustfmt_config as config;
extern crate rustfmt_core as rustfmt;
use std::env;
use std::path::{Path, PathBuf};
@ -12,7 +23,6 @@ use std::str::FromStr;
use getopts::{Matches, Options};
use rustfmt::{run, Input};
use rustfmt::config;
fn prune_files(files: Vec<&str>) -> Vec<&str> {
let prefixes: Vec<_> = files

20
rustfmt-bin/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "rustfmt-bin"
version = "0.4.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
build = "build.rs"
categories = ["development-tools"]
[[bin]]
name = "rustfmt"
path = "src/main.rs"
[dependencies]
env_logger = "0.4"
getopts = "0.2"
rustfmt-config = { path = "../rustfmt-config" }
rustfmt-core = { path = "../rustfmt-core" }

49
rustfmt-bin/build.rs Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out_dir.join("commit-info.txt"))
.unwrap()
.write_all(commit_info().as_bytes())
.unwrap();
}
// Try to get hash and date of the last commit on a best effort basis. If anything goes wrong
// (git not installed or if this is not a git repository) just return an empty string.
fn commit_info() -> String {
match (commit_hash(), commit_date()) {
(Some(hash), Some(date)) => format!(" ({} {})", hash.trim_right(), date),
_ => String::new(),
}
}
fn commit_hash() -> Option<String> {
Command::new("git")
.args(&["rev-parse", "--short", "HEAD"])
.output()
.ok()
.and_then(|r| String::from_utf8(r.stdout).ok())
}
fn commit_date() -> Option<String> {
Command::new("git")
.args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
.output()
.ok()
.and_then(|r| String::from_utf8(r.stdout).ok())
}

View File

@ -8,24 +8,25 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private)]
#![cfg(not(test))]
extern crate env_logger;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;
extern crate rustfmt_config as config;
extern crate rustfmt_core as rustfmt;
use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use getopts::{Matches, Options};
use config::{get_toml_path, Color, Config, WriteMode};
use config::file_lines::FileLines;
use rustfmt::{run, FileName, Input, Summary};
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};
use rustfmt::file_lines::FileLines;
use std::str::FromStr;
type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;
@ -387,7 +388,7 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) {
fn print_version() {
println!(
"{}-nightly{}",
env!("CARGO_PKG_VERSION"),
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
)
}
@ -453,7 +454,7 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
return Ok(Operation::Stdin {
input: buffer,
config_path: config_path,
config_path,
});
}
@ -469,8 +470,8 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
.collect();
Ok(Operation::Format {
files: files,
config_path: config_path,
minimal_config_path: minimal_config_path,
files,
config_path,
minimal_config_path,
})
}

16
rustfmt-config/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "rustfmt-config"
version = "0.4.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "A library for configuring and customizing rustfmt"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
categories = ["development-tools"]
[dependencies]
rustc-ap-syntax = "29.0.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
toml = "0.4"

View File

@ -0,0 +1,380 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use file_lines::FileLines;
use options::WidthHeuristics;
/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
/// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
/// pipe-separated list of variants; for other types it returns "<type>".
fn doc_hint() -> String;
}
impl ConfigType for bool {
fn doc_hint() -> String {
String::from("<boolean>")
}
}
impl ConfigType for usize {
fn doc_hint() -> String {
String::from("<unsigned integer>")
}
}
impl ConfigType for isize {
fn doc_hint() -> String {
String::from("<signed integer>")
}
}
impl ConfigType for String {
fn doc_hint() -> String {
String::from("<string>")
}
}
impl ConfigType for FileLines {
fn doc_hint() -> String {
String::from("<json>")
}
}
impl ConfigType for WidthHeuristics {
fn doc_hint() -> String {
String::new()
}
}
/// Check if we're in a nightly build.
///
/// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap
/// to "stable", "beta", or "nightly" depending on what toolchain is being built.
/// If we are being built as part of the stable or beta toolchains, we want
/// to disable unstable configuration options.
///
/// If we're being built by cargo (e.g. `cargo +nightly install rustfmt-nightly`),
/// `CFG_RELEASE_CHANNEL` is not set. As we only support being built against the
/// nightly compiler when installed from crates.io, default to nightly mode.
macro_rules! is_nightly_channel {
() => {
option_env!("CFG_RELEASE_CHANNEL")
.map(|c| c == "nightly")
.unwrap_or(true)
};
}
macro_rules! create_config {
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[derive(Clone)]
pub struct Config {
// For each config item, we store a bool indicating whether it has
// been accessed and the value, and a bool whether the option was
// manually initialised, or taken from the default,
$($i: (Cell<bool>, bool, $ty, bool)),+
}
// Just like the Config struct but with each property wrapped
// as Option<T>. This is used to parse a rustfmt.toml that doesn't
// specify all properties of `Config`.
// We first parse into `PartialConfig`, then create a default `Config`
// and overwrite the properties with corresponding values from `PartialConfig`.
#[derive(Deserialize, Serialize, Clone)]
pub struct PartialConfig {
$(pub $i: Option<$ty>),+
}
impl PartialConfig {
pub fn to_toml(&self) -> Result<String, String> {
// Non-user-facing options can't be specified in TOML
let mut cloned = self.clone();
cloned.file_lines = None;
cloned.verbose = None;
cloned.width_heuristics = None;
toml::to_string(&cloned)
.map_err(|e| format!("Could not output config: {}", e.to_string()))
}
}
// Macro hygiene won't allow us to make `set_$i()` methods on Config
// for each item, so this struct is used to give the API to set values:
// `config.set().option(false)`. It's pretty ugly. Consider replacing
// with `config.set_option(false)` if we ever get a stable/usable
// `concat_idents!()`.
pub struct ConfigSetter<'a>(&'a mut Config);
impl<'a> ConfigSetter<'a> {
$(
pub fn $i(&mut self, value: $ty) {
(self.0).$i.2 = value;
if stringify!($i) == "use_small_heuristics" {
self.0.set_heuristics();
}
}
)+
}
// Query each option, returns true if the user set the option, false if
// a default was used.
pub struct ConfigWasSet<'a>(&'a Config);
impl<'a> ConfigWasSet<'a> {
$(
pub fn $i(&self) -> bool {
(self.0).$i.1
}
)+
}
impl Config {
pub fn version_meets_requirement(&self, error_summary: &mut Summary) -> bool {
if self.was_set().required_version() {
let version = env!("CARGO_PKG_VERSION");
let required_version = self.required_version();
if version != required_version {
println!(
"Error: rustfmt version ({}) doesn't match the required version ({})",
version,
required_version,
);
error_summary.add_formatting_error();
return false;
}
}
true
}
$(
pub fn $i(&self) -> $ty {
self.$i.0.set(true);
self.$i.2.clone()
}
)+
pub fn set<'a>(&'a mut self) -> ConfigSetter<'a> {
ConfigSetter(self)
}
pub fn was_set<'a>(&'a self) -> ConfigWasSet<'a> {
ConfigWasSet(self)
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
$(
if let Some(val) = parsed.$i {
if self.$i.3 {
self.$i.1 = true;
self.$i.2 = val;
} else {
if is_nightly_channel!() {
self.$i.1 = true;
self.$i.2 = val;
} else {
eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
available in nightly channel.", stringify!($i), val);
}
}
}
)+
self.set_heuristics();
self
}
pub fn from_toml(toml: &str) -> Result<Config, String> {
let parsed: toml::Value =
toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
let mut err: String = String::new();
{
let table = parsed
.as_table()
.ok_or(String::from("Parsed config was not table"))?;
for key in table.keys() {
match &**key {
$(
stringify!($i) => (),
)+
_ => {
let msg =
&format!("Warning: Unknown configuration option `{}`\n", key);
err.push_str(msg)
}
}
}
}
match parsed.try_into() {
Ok(parsed_config) => {
if !err.is_empty() {
eprint!("{}", err);
}
Ok(Config::default().fill_from_parsed_config(parsed_config))
}
Err(e) => {
err.push_str("Error: Decoding config file failed:\n");
err.push_str(format!("{}\n", e).as_str());
err.push_str("Please check your config file.");
Err(err)
}
}
}
pub fn used_options(&self) -> PartialConfig {
PartialConfig {
$(
$i: if self.$i.0.get() {
Some(self.$i.2.clone())
} else {
None
},
)+
}
}
pub fn all_options(&self) -> PartialConfig {
PartialConfig {
$(
$i: Some(self.$i.2.clone()),
)+
}
}
pub fn override_value(&mut self, key: &str, val: &str)
{
match key {
$(
stringify!($i) => {
self.$i.2 = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!($ty)));
}
)+
_ => panic!("Unknown config key in override: {}", key)
}
if key == "use_small_heuristics" {
self.set_heuristics();
}
}
/// Construct a `Config` from the toml file specified at `file_path`.
///
/// This method only looks at the provided path, for a method that
/// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`.
///
/// Return a `Config` if the config could be read and parsed from
/// the file, Error otherwise.
pub fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
let mut file = File::open(&file_path)?;
let mut toml = String::new();
file.read_to_string(&mut toml)?;
Config::from_toml(&toml).map_err(|err| Error::new(ErrorKind::InvalidData, err))
}
/// Resolve the config for input in `dir`.
///
/// Searches for `rustfmt.toml` beginning with `dir`, and
/// recursively checking parents of `dir` if no config file is found.
/// If no config file exists in `dir` or in any parent, a
/// default `Config` will be returned (and the returned path will be empty).
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
pub fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
/// Try to find a project file in the given directory and its parents.
/// Returns the path of a the nearest project file if one exists,
/// or `None` if no project file was found.
fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
let mut current = if dir.is_relative() {
env::current_dir()?.join(dir)
} else {
dir.to_path_buf()
};
current = fs::canonicalize(current)?;
loop {
match get_toml_path(&current) {
Ok(Some(path)) => return Ok(Some(path)),
Err(e) => return Err(e),
_ => ()
}
// If the current directory has no parent, we're done searching.
if !current.pop() {
return Ok(None);
}
}
}
match resolve_project_file(dir)? {
None => Ok((Config::default(), None)),
Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
}
}
pub fn print_docs() {
use std::cmp;
const HIDE_OPTIONS: [&str; 3] = ["verbose", "file_lines", "width_heuristics"];
let max = 0;
$( let max = cmp::max(max, stringify!($i).len()+1); )+
let mut space_str = String::with_capacity(max);
for _ in 0..max {
space_str.push(' ');
}
println!("Configuration Options:");
$(
let name_raw = stringify!($i);
if !HIDE_OPTIONS.contains(&name_raw) {
let mut name_out = String::with_capacity(max);
for _ in name_raw.len()..max-1 {
name_out.push(' ')
}
name_out.push_str(name_raw);
name_out.push(' ');
println!("{}{} Default: {:?}",
name_out,
<$ty>::doc_hint(),
$def);
$(
println!("{}{}", space_str, $dstring);
)+
println!();
}
)+
}
fn set_heuristics(&mut self) {
if self.use_small_heuristics.2 {
self.set().width_heuristics(WidthHeuristics::default());
} else {
self.set().width_heuristics(WidthHeuristics::null());
}
}
}
// Template for the default configuration
impl Default for Config {
fn default() -> Config {
Config {
$(
$i: (Cell::new(false), false, $def, $stb),
)+
}
}
}
)
}

View File

@ -12,12 +12,25 @@
use std::{cmp, iter, str};
use std::collections::HashMap;
use std::rc::Rc;
use serde::de::{Deserialize, Deserializer};
use serde_json as json;
use codemap::LineRange;
use syntax::codemap::FileName;
use syntax::codemap::{FileMap, FileName};
/// A range of lines in a file, inclusive of both ends.
pub struct LineRange {
pub file: Rc<FileMap>,
pub lo: usize,
pub hi: usize,
}
impl LineRange {
pub fn file_name(&self) -> &FileName {
&self.file.name
}
}
/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
@ -34,7 +47,7 @@ impl<'a> From<&'a LineRange> for Range {
impl Range {
pub fn new(lo: usize, hi: usize) -> Range {
Range { lo: lo, hi: hi }
Range { lo, hi }
}
fn is_empty(self) -> bool {

252
rustfmt-config/src/lib.rs Normal file
View File

@ -0,0 +1,252 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate syntax;
extern crate toml;
use std::{env, fs};
use std::cell::Cell;
use std::default::Default;
use std::fs::File;
use std::io::{Error, ErrorKind, Read};
use std::path::{Path, PathBuf};
#[macro_use]
mod config_type;
#[macro_use]
mod options;
pub mod file_lines;
pub mod lists;
pub mod summary;
use config_type::ConfigType;
use file_lines::FileLines;
pub use lists::*;
pub use options::*;
use summary::Summary;
/// This macro defines configuration options used in rustfmt. Each option
/// is defined as follows:
///
/// `name: value type, default value, is stable, description;`
create_config! {
// Fundamental stuff
max_width: usize, 100, true, "Maximum width of each line";
hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
tab_spaces: usize, 4, true, "Number of spaces per tab";
newline_style: NewlineStyle, NewlineStyle::Unix, true, "Unix or Windows line endings";
indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items.";
use_small_heuristics: bool, true, false, "Whether to use different formatting for items and\
expressions if they satisfy a heuristic notion of 'small'.";
// strings and comments
format_strings: bool, false, false, "Format string literals where necessary";
wrap_comments: bool, false, true, "Break comments to fit on the line";
comment_width: usize, 80, false,
"Maximum length of comments. No effect unless wrap_comments = true";
normalize_comments: bool, false, true, "Convert /* */ comments to // comments where possible";
// Single line expressions and items.
empty_item_single_line: bool, true, false,
"Put empty-body functions and impls on a single line";
struct_lit_single_line: bool, true, false,
"Put small struct literals on a single line";
fn_single_line: bool, false, false, "Put single-expression functions on a single line";
where_single_line: bool, false, false, "To force single line where layout";
// Imports
imports_indent: IndentStyle, IndentStyle::Visual, false, "Indent of imports";
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
// Ordering
reorder_extern_crates: bool, true, false, "Reorder extern crate statements alphabetically";
reorder_extern_crates_in_group: bool, true, false, "Reorder extern crate statements in group";
reorder_imports: bool, false, false, "Reorder import statements alphabetically";
reorder_imports_in_group: bool, false, false, "Reorder import statements in group";
reorder_imported_names: bool, true, false,
"Reorder lists of names in import statements alphabetically";
reorder_modules: bool, false, false, "Reorder module statemtents alphabetically in group";
// Spaces around punctuation
binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
"Where to put a binary operator when a binary expression goes multiline.";
type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
space_before_colon: bool, false, false, "Leave a space before the colon";
space_after_colon: bool, true, false, "Leave a space after the colon";
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ... range operators";
spaces_within_parens_and_brackets: bool, false, false,
"Put spaces within non-empty parentheses or brackets";
// Misc.
combine_control_expr: bool, true, false, "Combine control expressions with function calls.";
struct_field_align_threshold: usize, 0, false, "Align struct fields if their diffs fits within \
threshold.";
remove_blank_lines_at_start_or_end_of_block: bool, true, false,
"Remove blank lines at start or end of a block";
match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
the same line with the pattern of arms";
force_multiline_blocks: bool, false, false,
"Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_density: Density, Density::Tall, false, "Argument density in functions";
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
"Brace style for control flow constructs";
trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
"How to handle trailing commas for lists";
trailing_semicolon: bool, true, false,
"Add trailing semicolon after break, continue and return";
match_block_trailing_comma: bool, false, false,
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
blank_lines_upper_bound: usize, 1, false,
"Maximum number of blank lines which can be put between items.";
blank_lines_lower_bound: usize, 0, false,
"Minimum number of blank lines which must be put between items.";
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
in tuple patterns";
force_explicit_abi: bool, true, true, "Always print the abi for extern items";
use_field_init_shorthand: bool, false, false, "Use field initialization shorthand if possible";
// Control options (changes the operation of rustfmt, rather than the formatting)
write_mode: WriteMode, WriteMode::Overwrite, false,
"What Write Mode to use when none is supplied: \
Replace, Overwrite, Display, Plain, Diff, Coverage";
color: Color, Color::Auto, false,
"What Color option to use when none is supplied: Always, Never, Auto";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
"Require a specific version of rustfmt.";
unstable_features: bool, false, true,
"Enables unstable features. Only available on nightly channel";
disable_all_formatting: bool, false, false, "Don't reformat anything";
skip_children: bool, false, false, "Don't reformat out of line modules";
hide_parse_errors: bool, false, false, "Hide errors from the parser";
error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
error_on_unformatted: bool, false, false,
"Error if unable to get comments or string literals within max_width, \
or they are left with trailing whitespaces";
report_todo: ReportTactic, ReportTactic::Never, false,
"Report all, none or unnumbered occurrences of TODO in source file comments";
report_fixme: ReportTactic, ReportTactic::Never, false,
"Report all, none or unnumbered occurrences of FIXME in source file comments";
// Not user-facing.
verbose: bool, false, false, "Use verbose output";
file_lines: FileLines, FileLines::all(), false,
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option";
width_heuristics: WidthHeuristics, WidthHeuristics::default(), false,
"'small' heuristic values";
}
/// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
///
/// Return the path if a config file exists, empty if no file exists, and Error for IO errors
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
for config_file_name in &CONFIG_FILE_NAMES {
let config_file = dir.join(config_file_name);
match fs::metadata(&config_file) {
// Only return if it's a file to handle the unlikely situation of a directory named
// `rustfmt.toml`.
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
Err(e) => {
if e.kind() != ErrorKind::NotFound {
return Err(e);
}
}
_ => {}
}
}
Ok(None)
}
#[cfg(test)]
mod test {
use super::Config;
#[test]
fn test_config_set() {
let mut config = Config::default();
config.set().verbose(false);
assert_eq!(config.verbose(), false);
config.set().verbose(true);
assert_eq!(config.verbose(), true);
}
#[test]
fn test_config_used_to_toml() {
let config = Config::default();
let merge_derives = config.merge_derives();
let skip_children = config.skip_children();
let used_options = config.used_options();
let toml = used_options.to_toml().unwrap();
assert_eq!(
toml,
format!(
"merge_derives = {}\nskip_children = {}\n",
merge_derives, skip_children,
)
);
}
#[test]
fn test_was_set() {
let config = Config::from_toml("hard_tabs = true").unwrap();
assert_eq!(config.was_set().hard_tabs(), true);
assert_eq!(config.was_set().verbose(), false);
}
// FIXME(#2183) these tests cannot be run in parallel because they use env vars
// #[test]
// fn test_as_not_nightly_channel() {
// let mut config = Config::default();
// assert_eq!(config.was_set().unstable_features(), false);
// config.set().unstable_features(true);
// assert_eq!(config.was_set().unstable_features(), false);
// }
// #[test]
// fn test_as_nightly_channel() {
// let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
// ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
// let mut config = Config::default();
// config.set().unstable_features(true);
// assert_eq!(config.was_set().unstable_features(), false);
// config.set().unstable_features(true);
// assert_eq!(config.unstable_features(), true);
// ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
// }
// #[test]
// fn test_unstable_from_toml() {
// let mut config = Config::from_toml("unstable_features = true").unwrap();
// assert_eq!(config.was_set().unstable_features(), false);
// let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
// ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
// config = Config::from_toml("unstable_features = true").unwrap();
// assert_eq!(config.was_set().unstable_features(), true);
// assert_eq!(config.unstable_features(), true);
// ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
// }
}

105
rustfmt-config/src/lists.rs Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Configuration options related to rewriting a list.
use IndentStyle;
use config_type::ConfigType;
/// The definitive formatting tactic for lists.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum DefinitiveListTactic {
Vertical,
Horizontal,
Mixed,
/// Special case tactic for `format!()`, `write!()` style macros.
SpecialMacro(usize),
}
impl DefinitiveListTactic {
pub fn ends_with_newline(&self, indent_style: IndentStyle) -> bool {
match indent_style {
IndentStyle::Block => *self != DefinitiveListTactic::Horizontal,
IndentStyle::Visual => false,
}
}
}
/// Formatting tactic for lists. This will be cast down to a
/// `DefinitiveListTactic` depending on the number and length of the items and
/// their comments.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum ListTactic {
// One item per row.
Vertical,
// All items on one row.
Horizontal,
// Try Horizontal layout, if that fails then vertical.
HorizontalVertical,
// HorizontalVertical with a soft limit of n characters.
LimitedHorizontalVertical(usize),
// Pack as many items as possible per row over (possibly) many rows.
Mixed,
}
impl_enum_serialize_and_deserialize!(ListTactic, Vertical, Horizontal, HorizontalVertical, Mixed);
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SeparatorTactic {
Always,
Never,
Vertical,
}
impl_enum_serialize_and_deserialize!(SeparatorTactic, Always, Never, Vertical);
impl SeparatorTactic {
pub fn from_bool(b: bool) -> SeparatorTactic {
if b {
SeparatorTactic::Always
} else {
SeparatorTactic::Never
}
}
}
/// Where to put separator.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SeparatorPlace {
Front,
Back,
}
impl_enum_serialize_and_deserialize!(SeparatorPlace, Front, Back);
impl SeparatorPlace {
pub fn is_front(&self) -> bool {
*self == SeparatorPlace::Front
}
pub fn is_back(&self) -> bool {
*self == SeparatorPlace::Back
}
pub fn from_tactic(
default: SeparatorPlace,
tactic: DefinitiveListTactic,
sep: &str,
) -> SeparatorPlace {
match tactic {
DefinitiveListTactic::Vertical => default,
_ => if sep == "," {
SeparatorPlace::Back
} else {
default
},
}
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

View File

@ -0,0 +1,246 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use config_type::ConfigType;
use lists::*;
/// Macro for deriving implementations of Serialize/Deserialize for enums
#[macro_export]
macro_rules! impl_enum_serialize_and_deserialize {
( $e:ident, $( $x:ident ),* ) => {
impl ::serde::ser::Serialize for $e {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ::serde::ser::Serializer
{
use serde::ser::Error;
// We don't know whether the user of the macro has given us all options.
#[allow(unreachable_patterns)]
match *self {
$(
$e::$x => serializer.serialize_str(stringify!($x)),
)*
_ => {
Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
}
}
}
}
impl<'de> ::serde::de::Deserialize<'de> for $e {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where D: ::serde::Deserializer<'de> {
use serde::de::{Error, Visitor};
use std::marker::PhantomData;
use std::fmt;
struct StringOnly<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for StringOnly<T>
where T: ::serde::Deserializer<'de> {
type Value = String;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string")
}
fn visit_str<E>(self, value: &str) -> Result<String, E> {
Ok(String::from(value))
}
}
let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
$(
if stringify!($x).eq_ignore_ascii_case(&s) {
return Ok($e::$x);
}
)*
static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
Err(D::Error::unknown_variant(&s, ALLOWED))
}
}
impl ::std::str::FromStr for $e {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$(
if stringify!($x).eq_ignore_ascii_case(s) {
return Ok($e::$x);
}
)*
Err("Bad variant")
}
}
impl ConfigType for $e {
fn doc_hint() -> String {
let mut variants = Vec::new();
$(
variants.push(stringify!($x));
)*
format!("[{}]", variants.join("|"))
}
}
};
}
macro_rules! configuration_option_enum{
($e:ident: $( $x:ident ),+ $(,)*) => {
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum $e {
$( $x ),+
}
impl_enum_serialize_and_deserialize!($e, $( $x ),+);
}
}
configuration_option_enum! { NewlineStyle:
Windows, // \r\n
Unix, // \n
Native, // \r\n in Windows, \n on other platforms
}
configuration_option_enum! { BraceStyle:
AlwaysNextLine,
PreferSameLine,
// Prefer same line except where there is a where clause, in which case force
// the brace to the next line.
SameLineWhere,
}
configuration_option_enum! { ControlBraceStyle:
// K&R style, Rust community default
AlwaysSameLine,
// Stroustrup style
ClosingNextLine,
// Allman style
AlwaysNextLine,
}
configuration_option_enum! { IndentStyle:
// First line on the same line as the opening brace, all lines aligned with
// the first line.
Visual,
// First line is on a new line and all lines align with block indent.
Block,
}
configuration_option_enum! { Density:
// Fit as much on one line as possible.
Compressed,
// Use more lines.
Tall,
// Place every item on a separate line.
Vertical,
}
configuration_option_enum! { TypeDensity:
// No spaces around "=" and "+"
Compressed,
// Spaces around " = " and " + "
Wide,
}
impl Density {
pub fn to_list_tactic(self) -> ListTactic {
match self {
Density::Compressed => ListTactic::Mixed,
Density::Tall => ListTactic::HorizontalVertical,
Density::Vertical => ListTactic::Vertical,
}
}
}
configuration_option_enum! { ReportTactic:
Always,
Unnumbered,
Never,
}
configuration_option_enum! { WriteMode:
// Backs the original file up and overwrites the original.
Replace,
// Overwrites original file without backup.
Overwrite,
// Writes the output to stdout.
Display,
// Writes the diff to stdout.
Diff,
// Displays how much of the input file was processed
Coverage,
// Unfancy stdout
Plain,
// Outputs a checkstyle XML file.
Checkstyle,
// Output the changed lines
Modified,
}
configuration_option_enum! { Color:
// Always use color, whether it is a piped or terminal output
Always,
// Never use color
Never,
// Automatically use color, if supported by terminal
Auto,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct WidthHeuristics {
// Maximum width of the args of a function call before falling back
// to vertical formatting.
pub fn_call_width: usize,
// Maximum width in the body of a struct lit before falling back to
// vertical formatting.
pub struct_lit_width: usize,
// Maximum width in the body of a struct variant before falling back
// to vertical formatting.
pub struct_variant_width: usize,
// Maximum width of an array literal before falling back to vertical
// formatting.
pub array_width: usize,
// Maximum length of a chain to fit on a single line.
pub chain_width: usize,
// Maximum line length for single line if-else expressions. A value
// of zero means always break if-else expressions.
pub single_line_if_else_max_width: usize,
}
impl WidthHeuristics {
// Using this WidthHeuristics means we ignore heuristics.
pub fn null() -> WidthHeuristics {
WidthHeuristics {
fn_call_width: usize::max_value(),
struct_lit_width: 0,
struct_variant_width: 0,
array_width: usize::max_value(),
chain_width: usize::max_value(),
single_line_if_else_max_width: 0,
}
}
}
impl Default for WidthHeuristics {
fn default() -> WidthHeuristics {
WidthHeuristics {
fn_call_width: 60,
struct_lit_width: 18,
struct_variant_width: 35,
array_width: 60,
chain_width: 60,
single_line_if_else_max_width: 50,
}
}
}
impl ::std::str::FromStr for WidthHeuristics {
type Err = &'static str;
fn from_str(_: &str) -> Result<Self, Self::Err> {
Err("WidthHeuristics is not parsable")
}
}

View File

@ -1,3 +1,13 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::time::{Duration, Instant};
use std::default::Default;

33
rustfmt-core/Cargo.toml Normal file
View File

@ -0,0 +1,33 @@
[package]
name = "rustfmt-core"
version = "0.4.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "A core library of rustfmt"
repository = "https://github.com/rust-lang-nursery/rustfmt"
readme = "README.md"
license = "Apache-2.0/MIT"
categories = ["development-tools"]
[lib]
doctest = false
[dependencies]
derive-new = "0.5"
diff = "0.1"
log = "0.3"
regex = "0.2"
rustc-ap-syntax = "29.0.0"
rustc-ap-rustc_errors = "29.0.0"
rustfmt-config = { path = "../rustfmt-config" }
term = "0.4"
unicode-segmentation = "1.0.0"
[dev-dependencies]
lazy_static = "1.0.0"
[target.'cfg(unix)'.dependencies]
libc = "0.2.11"
[target.'cfg(windows)'.dependencies]
kernel32-sys = "0.2.2"
winapi = "0.2.7"

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use config::lists::*;
use syntax::{ast, ptr};
use syntax::codemap::Span;
use syntax::parse::classify;
@ -15,8 +16,7 @@ use syntax::parse::classify;
use codemap::SpanUtils;
use expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond, ToExpr};
use items::{span_hi_for_arg, span_lo_for_arg};
use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
ListTactic, Separator, SeparatorPlace, SeparatorTactic};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use utils::{last_line_width, left_most_sub_expr, stmt_expr};
@ -33,6 +33,7 @@ use utils::{last_line_width, left_most_sub_expr, stmt_expr};
pub fn rewrite_closure(
capture: ast::CaptureBy,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
span: Span,
@ -42,7 +43,7 @@ pub fn rewrite_closure(
debug!("rewrite_closure {:?}", body);
let (prefix, extra_offset) =
rewrite_closure_fn_decl(capture, fn_decl, body, span, context, shape)?;
rewrite_closure_fn_decl(capture, movability, fn_decl, body, span, context, shape)?;
// 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset)?;
@ -194,6 +195,7 @@ fn rewrite_closure_block(
// Return type is (prefix, extra_offset)
fn rewrite_closure_fn_decl(
capture: ast::CaptureBy,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
span: Span,
@ -205,9 +207,17 @@ fn rewrite_closure_fn_decl(
} else {
""
};
let immovable = if movability == ast::Movability::Static {
"static "
} else {
""
};
// 4 = "|| {".len(), which is overconservative when the closure consists of
// a single expression.
let nested_shape = shape.shrink_left(mover.len())?.sub_width(4)?;
let nested_shape = shape
.shrink_left(mover.len() + immovable.len())?
.sub_width(4)?;
// 1 = |
let argument_offset = nested_shape.indent + 1;
@ -244,7 +254,7 @@ fn rewrite_closure_fn_decl(
};
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: SeparatorTactic::Never,
separator_place: SeparatorPlace::Back,
@ -254,7 +264,7 @@ fn rewrite_closure_fn_decl(
config: context.config,
};
let list_str = write_list(&item_vec, &fmt)?;
let mut prefix = format!("{}|{}|", mover, list_str);
let mut prefix = format!("{}{}|{}|", immovable, mover, list_str);
if !ret_str.is_empty() {
if prefix.contains('\n') {
@ -278,7 +288,7 @@ pub fn rewrite_last_closure(
expr: &ast::Expr,
shape: Shape,
) -> Option<String> {
if let ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) = expr.node {
if let ast::ExprKind::Closure(capture, movability, ref fn_decl, ref body, _) = expr.node {
let body = match body.node {
ast::ExprKind::Block(ref block)
if !is_unsafe_block(block) && is_simple_block(block, context.codemap) =>
@ -287,8 +297,15 @@ pub fn rewrite_last_closure(
}
_ => body,
};
let (prefix, extra_offset) =
rewrite_closure_fn_decl(capture, fn_decl, body, expr.span, context, shape)?;
let (prefix, extra_offset) = rewrite_closure_fn_decl(
capture,
movability,
fn_decl,
body,
expr.span,
context,
shape,
)?;
// If the closure goes multi line before its body, do not overflow the closure.
if prefix.contains('\n') {
return None;

View File

@ -11,25 +11,11 @@
//! This module contains utilities that work with the `CodeMap` from `libsyntax` / `syntex_syntax`.
//! This includes extension traits and methods for looking up spans and line ranges for AST nodes.
use std::rc::Rc;
use syntax::codemap::{BytePos, CodeMap, FileMap, FileName, Span};
use config::file_lines::LineRange;
use syntax::codemap::{BytePos, CodeMap, Span};
use comment::FindUncommented;
/// A range of lines in a file, inclusive of both ends.
pub struct LineRange {
pub file: Rc<FileMap>,
pub lo: usize,
pub hi: usize,
}
impl LineRange {
pub fn file_name(&self) -> &FileName {
&self.file.name
}
}
pub trait SpanUtils {
fn span_after(&self, original: Span, needle: &str) -> BytePos;
fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
@ -48,8 +34,8 @@ pub trait LineRangeUtils {
impl SpanUtils for CodeMap {
fn span_after(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
let snippet = self.span_to_snippet(original).expect("Bad snippet");
let offset = snippet.find_uncommented(needle).expect("Bad offset") + needle.len();
original.lo() + BytePos(offset as u32)
}

View File

@ -134,6 +134,11 @@ fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle {
}
}
/// Combine `prev_str` and `next_str` into a single `String`. `span` may contain
/// comments between two strings. If there are such comments, then that will be
/// recovered. If `allow_extend` is true and there is no comment between the two
/// strings, then they will be put on a single line as long as doing so does not
/// exceed max width.
pub fn combine_strs_with_missing_comments(
context: &RewriteContext,
prev_str: &str,
@ -285,11 +290,11 @@ fn rewrite_comment_inner(
let mut fmt = StringFormat {
opener: "",
closer: "",
line_start: line_start,
line_start,
line_end: "",
shape: Shape::legacy(max_chars, fmt_indent),
trim_end: true,
config: config,
config,
};
let line_breaks = count_newlines(orig.trim_right());
@ -328,7 +333,7 @@ fn rewrite_comment_inner(
while let Some(line) = iter.next() {
result.push_str(line);
result.push_str(match iter.peek() {
Some(ref next_line) if next_line.is_empty() => comment_line_separator.trim_right(),
Some(next_line) if next_line.is_empty() => comment_line_separator.trim_right(),
Some(..) => &comment_line_separator,
None => "",
});
@ -895,7 +900,7 @@ pub struct CommentCodeSlices<'a> {
impl<'a> CommentCodeSlices<'a> {
pub fn new(slice: &'a str) -> CommentCodeSlices<'a> {
CommentCodeSlices {
slice: slice,
slice,
last_slice_kind: CodeCharKind::Comment,
last_slice_end: 0,
}
@ -1019,7 +1024,7 @@ impl<'a> CommentReducer<'a> {
let is_block = comment.starts_with("/*");
let comment = remove_comment_header(comment);
CommentReducer {
is_block: is_block,
is_block,
at_start_line: false, // There are no supplementary '*' on the first line
iter: comment.chars(),
}

View File

@ -12,6 +12,7 @@ use std::borrow::Cow;
use std::cmp::min;
use std::iter::repeat;
use config::lists::*;
use syntax::{ast, ptr};
use syntax::codemap::{BytePos, CodeMap, Span};
@ -22,8 +23,7 @@ use comment::{combine_strs_with_missing_comments, contains_comment, recover_comm
rewrite_comment, rewrite_missing_comment, FindUncommented};
use config::{Config, ControlBraceStyle, IndentStyle};
use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting,
struct_lit_shape, struct_lit_tactic, write_list, DefinitiveListTactic, ListFormatting,
ListItem, ListTactic, Separator, SeparatorPlace, SeparatorTactic};
struct_lit_shape, struct_lit_tactic, write_list, ListFormatting, ListItem, Separator};
use macros::{rewrite_macro, MacroArg, MacroPosition};
use patterns::{can_be_overflowed_pat, TuplePatField};
use rewrite::{Rewrite, RewriteContext};
@ -135,16 +135,16 @@ pub fn format_expr(
ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
rewrite_assignment(context, lhs, rhs, Some(op), shape)
}
ast::ExprKind::Continue(ref opt_ident) => {
let id_str = match *opt_ident {
Some(ident) => format!(" {}", ident.node),
ast::ExprKind::Continue(ref opt_label) => {
let id_str = match *opt_label {
Some(label) => format!(" {}", label.ident),
None => String::new(),
};
Some(format!("continue{}", id_str))
}
ast::ExprKind::Break(ref opt_ident, ref opt_expr) => {
let id_str = match *opt_ident {
Some(ident) => format!(" {}", ident.node),
ast::ExprKind::Break(ref opt_label, ref opt_expr) => {
let id_str = match *opt_label {
Some(label) => format!(" {}", label.ident),
None => String::new(),
};
@ -159,8 +159,16 @@ pub fn format_expr(
} else {
Some("yield".to_string())
},
ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
closures::rewrite_closure(capture, fn_decl, body, expr.span, context, shape)
ast::ExprKind::Closure(capture, movability, ref fn_decl, ref body, _) => {
closures::rewrite_closure(
capture,
movability,
fn_decl,
body,
expr.span,
context,
shape,
)
}
ast::ExprKind::Try(..)
| ast::ExprKind::Field(..)
@ -441,7 +449,7 @@ pub fn rewrite_array<T: Rewrite + Spanned + ToExpr>(
let ends_with_newline = tactic.ends_with_newline(context.config.indent_style());
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if trailing_comma {
SeparatorTactic::Always
@ -462,7 +470,7 @@ pub fn rewrite_array<T: Rewrite + Spanned + ToExpr>(
},
separator_place: SeparatorPlace::Back,
shape: nested_shape,
ends_with_newline: ends_with_newline,
ends_with_newline,
preserve_newline: false,
config: context.config,
};
@ -718,7 +726,7 @@ struct ControlFlow<'a> {
cond: Option<&'a ast::Expr>,
block: &'a ast::Block,
else_block: Option<&'a ast::Expr>,
label: Option<ast::SpannedIdent>,
label: Option<ast::Label>,
pat: Option<&'a ast::Pat>,
keyword: &'a str,
matcher: &'a str,
@ -779,39 +787,35 @@ impl<'a> ControlFlow<'a> {
) -> ControlFlow<'a> {
ControlFlow {
cond: Some(cond),
block: block,
else_block: else_block,
block,
else_block,
label: None,
pat: pat,
pat,
keyword: "if",
matcher: match pat {
Some(..) => "let",
None => "",
},
connector: " =",
allow_single_line: allow_single_line,
nested_if: nested_if,
span: span,
allow_single_line,
nested_if,
span,
}
}
fn new_loop(
block: &'a ast::Block,
label: Option<ast::SpannedIdent>,
span: Span,
) -> ControlFlow<'a> {
fn new_loop(block: &'a ast::Block, label: Option<ast::Label>, span: Span) -> ControlFlow<'a> {
ControlFlow {
cond: None,
block: block,
block,
else_block: None,
label: label,
label,
pat: None,
keyword: "loop",
matcher: "",
connector: "",
allow_single_line: false,
nested_if: false,
span: span,
span,
}
}
@ -819,15 +823,15 @@ impl<'a> ControlFlow<'a> {
pat: Option<&'a ast::Pat>,
cond: &'a ast::Expr,
block: &'a ast::Block,
label: Option<ast::SpannedIdent>,
label: Option<ast::Label>,
span: Span,
) -> ControlFlow<'a> {
ControlFlow {
cond: Some(cond),
block: block,
block,
else_block: None,
label: label,
pat: pat,
label,
pat,
keyword: "while",
matcher: match pat {
Some(..) => "let",
@ -836,7 +840,7 @@ impl<'a> ControlFlow<'a> {
connector: " =",
allow_single_line: false,
nested_if: false,
span: span,
span,
}
}
@ -844,21 +848,21 @@ impl<'a> ControlFlow<'a> {
pat: &'a ast::Pat,
cond: &'a ast::Expr,
block: &'a ast::Block,
label: Option<ast::SpannedIdent>,
label: Option<ast::Label>,
span: Span,
) -> ControlFlow<'a> {
ControlFlow {
cond: Some(cond),
block: block,
block,
else_block: None,
label: label,
label,
pat: Some(pat),
keyword: "for",
matcher: "",
connector: " in",
allow_single_line: false,
nested_if: false,
span: span,
span,
}
}
@ -1166,9 +1170,9 @@ impl<'a> Rewrite for ControlFlow<'a> {
}
}
fn rewrite_label(label: Option<ast::SpannedIdent>) -> Cow<'static, str> {
match label {
Some(ident) => Cow::from(format!("{}: ", ident.node)),
fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> {
match opt_label {
Some(label) => Cow::from(format!("{}: ", label.ident)),
None => Cow::from(""),
}
}
@ -1484,7 +1488,7 @@ fn rewrite_match_pattern(
)
};
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: " |",
trailing_separator: SeparatorTactic::Never,
separator_place: context.config.binop_separator(),
@ -1988,7 +1992,7 @@ where
);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if force_trailing_comma {
SeparatorTactic::Always
@ -2565,9 +2569,13 @@ pub fn rewrite_field(
if contains_skip(&field.attrs) {
return Some(context.snippet(field.span()).to_owned());
}
let name = &field.ident.node.to_string();
let mut attrs_str = field.attrs.rewrite(context, shape)?;
if !attrs_str.is_empty() {
attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
};
let name = field.ident.node.to_string();
if field.is_shorthand {
Some(name.to_string())
Some(attrs_str + &name)
} else {
let mut separator = String::from(struct_lit_field_separator(context.config));
for _ in 0..prefix_max_width.checked_sub(name.len()).unwrap_or(0) {
@ -2577,12 +2585,10 @@ pub fn rewrite_field(
let expr_shape = shape.offset_left(overhead)?;
let expr = field.expr.rewrite(context, expr_shape);
let mut attrs_str = field.attrs.rewrite(context, shape)?;
if !attrs_str.is_empty() {
attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
};
match expr {
Some(ref e) if e.as_str() == name && context.config.use_field_init_shorthand() => {
Some(attrs_str + &name)
}
Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
None => {
let expr_offset = shape.indent.block_indent(context.config);
@ -2671,11 +2677,11 @@ where
nested_shape.width,
);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: SeparatorTactic::Never,
separator_place: SeparatorPlace::Back,
shape: shape,
shape,
ends_with_newline: false,
preserve_newline: false,
config: context.config,

View File

@ -19,10 +19,7 @@ use config::{Config, NewlineStyle, WriteMode};
use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
use syntax::codemap::FileName;
// A map of the files of a crate, with their new content
pub type FileMap = Vec<FileRecord>;
pub type FileRecord = (FileName, String);
use FileRecord;
// Append a newline to the end of each file.
pub fn append_newline(s: &mut String) {

View File

@ -10,14 +10,14 @@
use std::cmp::Ordering;
use config::lists::*;
use syntax::ast;
use syntax::codemap::{BytePos, Span};
use codemap::SpanUtils;
use comment::combine_strs_with_missing_comments;
use config::IndentStyle;
use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
ListItem, Separator, SeparatorPlace, SeparatorTactic};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use spanned::Spanned;
@ -105,10 +105,13 @@ fn compare_use_trees(a: &ast::UseTree, b: &ast::UseTree, nested: bool) -> Orderi
}
}
fn compare_use_items(a: &ast::Item, b: &ast::Item) -> Option<Ordering> {
fn compare_use_items(a: &ast::Item, b: &ast::Item) -> Ordering {
match (&a.node, &b.node) {
(&ast::ItemKind::Mod(..), &ast::ItemKind::Mod(..)) => {
a.ident.name.as_str().cmp(&b.ident.name.as_str())
}
(&ast::ItemKind::Use(ref a_tree), &ast::ItemKind::Use(ref b_tree)) => {
Some(compare_use_trees(a_tree, b_tree, false))
compare_use_trees(a_tree, b_tree, false)
}
(&ast::ItemKind::ExternCrate(ref a_name), &ast::ItemKind::ExternCrate(ref b_name)) => {
// `extern crate foo as bar;`
@ -119,20 +122,19 @@ fn compare_use_items(a: &ast::Item, b: &ast::Item) -> Option<Ordering> {
b_name.map_or_else(|| b.ident.name.as_str(), |symbol| symbol.as_str());
let result = a_orig_name.cmp(&b_orig_name);
if result != Ordering::Equal {
return Some(result);
return result;
}
// `extern crate foo as bar;`
// ^^^ Comparing this.
let result = match (a_name, b_name) {
match (a_name, b_name) {
(Some(..), None) => Ordering::Greater,
(None, Some(..)) => Ordering::Less,
(None, None) => Ordering::Equal,
(Some(..), Some(..)) => a.ident.name.cmp(&b.ident.name),
};
Some(result)
(Some(..), Some(..)) => a.ident.name.as_str().cmp(&b.ident.name.as_str()),
}
}
_ => None,
_ => unreachable!(),
}
}
@ -232,6 +234,16 @@ fn rewrite_import(
}
}
/// Rewrite an inline mod.
fn rewrite_mod(item: &ast::Item) -> String {
let mut result = String::with_capacity(32);
result.push_str(&*format_visibility(&item.vis));
result.push_str("mod ");
result.push_str(&item.ident.to_string());
result.push(';');
result
}
fn rewrite_imports(
context: &RewriteContext,
use_items: &[&ast::Item],
@ -246,12 +258,13 @@ fn rewrite_imports(
|item| item.span().lo(),
|item| item.span().hi(),
|item| {
let attrs_str = item.attrs.rewrite(context, shape)?;
let attrs = ::visitor::filter_inline_attrs(&item.attrs, item.span());
let attrs_str = attrs.rewrite(context, shape)?;
let missed_span = if item.attrs.is_empty() {
let missed_span = if attrs.is_empty() {
mk_sp(item.span.lo(), item.span.lo())
} else {
mk_sp(item.attrs.last().unwrap().span.hi(), item.span.lo())
mk_sp(attrs.last().unwrap().span.hi(), item.span.lo())
};
let item_str = match item.node {
@ -259,6 +272,7 @@ fn rewrite_imports(
rewrite_import(context, &item.vis, tree, &item.attrs, shape)?
}
ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item)?,
ast::ItemKind::Mod(..) => rewrite_mod(item),
_ => return None,
};
@ -276,7 +290,7 @@ fn rewrite_imports(
false,
);
let mut item_pair_vec: Vec<_> = items.zip(use_items.iter()).collect();
item_pair_vec.sort_by(|a, b| compare_use_items(a.1, b.1).unwrap());
item_pair_vec.sort_by(|a, b| compare_use_items(a.1, b.1));
let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();
let fmt = ListFormatting {
@ -284,7 +298,7 @@ fn rewrite_imports(
separator: "",
trailing_separator: SeparatorTactic::Never,
separator_place: SeparatorPlace::Back,
shape: shape,
shape,
ends_with_newline: true,
preserve_newline: false,
config: context.config,
@ -537,7 +551,7 @@ fn rewrite_nested_use_tree(
&& tactic != DefinitiveListTactic::Horizontal;
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if ends_with_newline {
context.config.trailing_comma()
@ -546,7 +560,7 @@ fn rewrite_nested_use_tree(
},
separator_place: SeparatorPlace::Back,
shape: nested_shape,
ends_with_newline: ends_with_newline,
ends_with_newline,
preserve_newline: true,
config: context.config,
};

View File

@ -14,17 +14,15 @@
use std::fmt;
pub use config::ReportTactic;
use config::ReportTactic;
const TO_DO_CHARS: &[char] = &['t', 'o', 'd', 'o'];
const FIX_ME_CHARS: &[char] = &['f', 'i', 'x', 'm', 'e'];
// Enabled implementation detail is here because it is
// irrelevant outside the issues module
impl ReportTactic {
fn is_enabled(&self) -> bool {
*self != ReportTactic::Never
}
fn is_enabled(report_tactic: ReportTactic) -> bool {
report_tactic != ReportTactic::Never
}
#[derive(Clone, Copy)]
@ -90,8 +88,8 @@ impl BadIssueSeeker {
todo_idx: 0,
fixme_idx: 0,
},
report_todo: report_todo,
report_fixme: report_fixme,
report_todo,
report_fixme,
}
}
@ -128,7 +126,7 @@ impl BadIssueSeeker {
fn inspect_issue(&mut self, c: char, mut todo_idx: usize, mut fixme_idx: usize) -> Seeking {
if let Some(lower_case_c) = c.to_lowercase().next() {
if self.report_todo.is_enabled() && lower_case_c == TO_DO_CHARS[todo_idx] {
if is_enabled(self.report_todo) && lower_case_c == TO_DO_CHARS[todo_idx] {
todo_idx += 1;
if todo_idx == TO_DO_CHARS.len() {
return Seeking::Number {
@ -144,7 +142,7 @@ impl BadIssueSeeker {
};
}
fixme_idx = 0;
} else if self.report_fixme.is_enabled() && lower_case_c == FIX_ME_CHARS[fixme_idx] {
} else if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] {
// Exploit the fact that the character sets of todo and fixme
// are disjoint by adding else.
fixme_idx += 1;
@ -169,8 +167,8 @@ impl BadIssueSeeker {
}
Seeking::Issue {
todo_idx: todo_idx,
fixme_idx: fixme_idx,
todo_idx,
fixme_idx,
}
}
@ -213,10 +211,7 @@ impl BadIssueSeeker {
NumberPart::CloseParen => {}
}
self.state = Seeking::Number {
part: part,
issue: issue,
};
self.state = Seeking::Number { part, issue };
IssueClassification::None
}

View File

@ -13,6 +13,7 @@
use std::borrow::Cow;
use std::cmp::min;
use config::lists::*;
use syntax::{abi, ast, ptr, symbol};
use syntax::ast::{CrateSugar, ImplItem};
use syntax::codemap::{BytePos, Span};
@ -24,8 +25,7 @@ use comment::{combine_strs_with_missing_comments, contains_comment, recover_comm
use config::{BraceStyle, Config, Density, IndentStyle};
use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs,
rewrite_call_inner, ExprType};
use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
ListItem, ListTactic, Separator, SeparatorPlace, SeparatorTactic};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
use rewrite::{Rewrite, RewriteContext};
use shape::{Indent, Shape};
use spanned::Spanned;
@ -138,7 +138,7 @@ impl<'a> Item<'a> {
.iter()
.map(|i| BodyElement::ForeignItem(i))
.collect(),
span: span,
span,
}
}
}
@ -169,8 +169,8 @@ impl<'a> FnSig<'a> {
vis: ast::Visibility,
) -> FnSig<'a> {
FnSig {
decl: decl,
generics: generics,
decl,
generics,
abi: abi::Abi::Rust,
constness: ast::Constness::NotConst,
defaultness: ast::Defaultness::Final,
@ -189,7 +189,7 @@ impl<'a> FnSig<'a> {
defaultness: ast::Defaultness::Final,
abi: method_sig.abi,
decl: &*method_sig.decl,
generics: generics,
generics,
visibility: ast::Visibility::Inherited,
}
}
@ -202,12 +202,12 @@ impl<'a> FnSig<'a> {
) -> FnSig<'a> {
match *fn_kind {
visit::FnKind::ItemFn(_, unsafety, constness, abi, visibility, _) => FnSig {
decl: decl,
generics: generics,
abi: abi,
decl,
generics,
abi,
constness: constness.node,
defaultness: defualtness,
unsafety: unsafety,
unsafety,
visibility: visibility.clone(),
},
visit::FnKind::Method(_, method_sig, vis, _) => {
@ -504,13 +504,13 @@ impl<'a> FmtVisitor<'a> {
items = itemize_list_with(0);
}
let shape = self.shape().sub_width(2).unwrap();
let shape = self.shape().sub_width(2)?;
let fmt = ListFormatting {
tactic: DefinitiveListTactic::Vertical,
separator: ",",
trailing_separator: self.config.trailing_comma(),
separator_place: SeparatorPlace::Back,
shape: shape,
shape,
ends_with_newline: true,
preserve_newline: true,
config: self.config,
@ -558,14 +558,7 @@ impl<'a> FmtVisitor<'a> {
}
};
combine_strs_with_missing_comments(
&context,
&attrs_str,
&variant_body,
span,
shape,
is_attributes_extendable(&attrs_str),
)
combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false)
}
}
@ -895,10 +888,10 @@ impl<'a> StructParts<'a> {
_ => unreachable!(),
};
StructParts {
prefix: prefix,
prefix,
ident: item.ident,
vis: &item.vis,
def: def,
def,
generics: Some(generics),
span: item.span,
}
@ -1509,11 +1502,11 @@ impl<'a> StaticParts<'a> {
_ => unreachable!(),
};
StaticParts {
prefix: prefix,
prefix,
vis: &item.vis,
ident: item.ident,
ty: ty,
mutability: mutability,
ty,
mutability,
expr_opt: Some(expr),
defaultness: None,
span: item.span,
@ -1529,7 +1522,7 @@ impl<'a> StaticParts<'a> {
prefix: "const",
vis: &ast::Visibility::Inherited,
ident: ti.ident,
ty: ty,
ty,
mutability: ast::Mutability::Immutable,
expr_opt: expr_opt.as_ref(),
defaultness: None,
@ -1546,7 +1539,7 @@ impl<'a> StaticParts<'a> {
prefix: "const",
vis: &ii.vis,
ident: ii.ident,
ty: ty,
ty,
mutability: ast::Mutability::Immutable,
expr_opt: Some(expr),
defaultness: Some(ii.defaultness),
@ -1818,7 +1811,7 @@ fn rewrite_fn_base(
let one_line_budget = context.budget(used_width + overhead);
let shape = Shape {
width: one_line_budget,
indent: indent,
indent,
offset: used_width,
};
let fd = fn_sig.decl;
@ -2085,8 +2078,8 @@ struct WhereClauseOption {
impl WhereClauseOption {
pub fn new(suppress_comma: bool, snuggle: bool) -> WhereClauseOption {
WhereClauseOption {
suppress_comma: suppress_comma,
snuggle: snuggle,
suppress_comma,
snuggle,
compress_where: false,
}
}
@ -2233,7 +2226,7 @@ fn rewrite_args(
debug!("rewrite_args: budget: {}, tactic: {:?}", budget, tactic);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if variadic {
SeparatorTactic::Never
@ -2404,7 +2397,7 @@ where
one_line_budget,
);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if context.config.indent_style() == IndentStyle::Visual {
SeparatorTactic::Never
@ -2412,7 +2405,7 @@ where
context.config.trailing_comma()
},
separator_place: SeparatorPlace::Back,
shape: shape,
shape,
ends_with_newline: tactic.ends_with_newline(context.config.indent_style()),
preserve_newline: true,
config: context.config,
@ -2637,7 +2630,7 @@ fn rewrite_where_clause(
}
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: comma_tactic,
separator_place: SeparatorPlace::Back,

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(decl_macro)]
#![feature(match_default_bindings)]
#![feature(rustc_private)]
#![feature(type_ascription)]
#[macro_use]
@ -19,10 +19,7 @@ extern crate diff;
extern crate log;
extern crate regex;
extern crate rustc_errors as errors;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate rustfmt_config as config;
extern crate syntax;
extern crate term;
extern crate unicode_segmentation;
@ -44,43 +41,44 @@ use syntax::parse::{self, ParseSess};
use checkstyle::{output_footer, output_header};
use comment::{CharClasses, FullCodeCharKind};
pub use config::Config;
use filemap::FileMap;
use issues::{BadIssueSeeker, Issue};
use shape::Indent;
use utils::use_colored_tty;
use visitor::{FmtVisitor, SnippetProvider};
pub use self::summary::Summary;
pub use config::Config;
pub use config::summary::Summary;
#[macro_use]
mod utils;
mod shape;
mod spanned;
pub mod config;
pub mod codemap;
pub mod filemap;
pub mod file_lines;
pub mod visitor;
mod chains;
mod checkstyle;
mod closures;
mod items;
mod missed_spans;
mod lists;
mod types;
pub mod codemap;
mod comment;
mod expr;
pub mod filemap;
mod imports;
mod issues;
mod rewrite;
mod string;
mod comment;
pub mod modules;
pub mod rustfmt_diff;
mod chains;
mod items;
mod lists;
mod macros;
mod missed_spans;
pub mod modules;
mod patterns;
mod summary;
mod rewrite;
pub mod rustfmt_diff;
mod shape;
mod spanned;
mod string;
mod types;
mod vertical;
pub mod visitor;
// A map of the files of a crate, with their new content
pub type FileMap = Vec<FileRecord>;
pub type FileRecord = (FileName, String);
#[derive(Clone, Copy)]
pub enum ErrorKind {
@ -165,7 +163,7 @@ impl FormatReport {
self.file_error_map
.iter()
.map(|(_, errors)| errors.len())
.fold(0, |acc, x| acc + x)
.sum()
}
pub fn has_warnings(&self) -> bool {
@ -448,7 +446,7 @@ fn format_lines(
line: cur_line,
kind: error_kind,
is_comment: kind.is_comment(),
is_string: is_string,
is_string,
line_buffer: line_buffer.clone(),
});
}
@ -463,7 +461,7 @@ fn format_lines(
is_string = false;
} else {
newline_count = 0;
line_len += 1;
line_len += if c == '\t' { config.tab_spaces() } else { 1 };
if c.is_whitespace() {
if last_wspace.is_none() {
last_wspace = Some(b);
@ -570,7 +568,12 @@ pub fn format_code_block(code_snippet: &str, config: &Config) -> Option<String>
let indent_str =
Indent::from_width(config, config.tab_spaces()).to_string(config);
if line.starts_with(indent_str.as_ref()) {
&line[config.tab_spaces()..]
let offset = if config.hard_tabs() {
1
} else {
config.tab_spaces()
};
&line[offset..]
} else {
line
}
@ -605,10 +608,17 @@ pub fn format_input<T: Write>(
Box::new(Vec::new()),
Some(codemap.clone()),
false,
false,
));
Handler::with_emitter(true, false, silent_emitter)
} else {
Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()))
let supports_color = term::stderr().map_or(false, |term| term.supports_color());
let color_cfg = if supports_color {
ColorConfig::Auto
} else {
ColorConfig::Never
};
Handler::with_tty_emitter(color_cfg, true, false, Some(codemap.clone()))
};
let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());
@ -639,6 +649,7 @@ pub fn format_input<T: Write>(
Box::new(Vec::new()),
Some(codemap.clone()),
false,
false,
));
parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);

View File

@ -8,9 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Format list-like expressions and items.
use std::cmp;
use std::iter::Peekable;
use config::lists::*;
use syntax::codemap::{BytePos, CodeMap};
use comment::{find_comment_end, rewrite_comment, FindUncommented};
@ -19,44 +22,6 @@ use rewrite::RewriteContext;
use shape::{Indent, Shape};
use utils::{count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline};
/// Formatting tactic for lists. This will be cast down to a
/// `DefinitiveListTactic` depending on the number and length of the items and
/// their comments.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum ListTactic {
// One item per row.
Vertical,
// All items on one row.
Horizontal,
// Try Horizontal layout, if that fails then vertical.
HorizontalVertical,
// HorizontalVertical with a soft limit of n characters.
LimitedHorizontalVertical(usize),
// Pack as many items as possible per row over (possibly) many rows.
Mixed,
}
impl_enum_serialize_and_deserialize!(ListTactic, Vertical, Horizontal, HorizontalVertical, Mixed);
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SeparatorTactic {
Always,
Never,
Vertical,
}
impl_enum_serialize_and_deserialize!(SeparatorTactic, Always, Never, Vertical);
impl SeparatorTactic {
pub fn from_bool(b: bool) -> SeparatorTactic {
if b {
SeparatorTactic::Always
} else {
SeparatorTactic::Never
}
}
}
pub struct ListFormatting<'a> {
pub tactic: DefinitiveListTactic,
pub separator: &'a str,
@ -154,25 +119,6 @@ impl ListItem {
}
}
/// The definitive formatting tactic for lists.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum DefinitiveListTactic {
Vertical,
Horizontal,
Mixed,
/// Special case tactic for `format!()`, `write!()` style macros.
SpecialMacro(usize),
}
impl DefinitiveListTactic {
pub fn ends_with_newline(&self, indent_style: IndentStyle) -> bool {
match indent_style {
IndentStyle::Block => *self != DefinitiveListTactic::Horizontal,
IndentStyle::Visual => false,
}
}
}
/// The type of separator for lists.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Separator {
@ -191,40 +137,6 @@ impl Separator {
}
}
/// Where to put separator.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SeparatorPlace {
Front,
Back,
}
impl_enum_serialize_and_deserialize!(SeparatorPlace, Front, Back);
impl SeparatorPlace {
pub fn is_front(&self) -> bool {
*self == SeparatorPlace::Front
}
pub fn is_back(&self) -> bool {
*self == SeparatorPlace::Back
}
pub fn from_tactic(
default: SeparatorPlace,
tactic: DefinitiveListTactic,
sep: &str,
) -> SeparatorPlace {
match tactic {
DefinitiveListTactic::Vertical => default,
_ => if sep == "," {
SeparatorPlace::Back
} else {
default
},
}
}
}
pub fn definitive_tactic<I, T>(
items: I,
tactic: ListTactic,
@ -690,15 +602,15 @@ where
};
ListItem {
pre_comment: pre_comment,
pre_comment_style: pre_comment_style,
pre_comment,
pre_comment_style,
item: if self.inner.peek().is_none() && self.leave_last {
None
} else {
(self.get_item_string)(&item)
},
post_comment: post_comment,
new_lines: new_lines,
post_comment,
new_lines,
}
})
}
@ -724,16 +636,16 @@ where
F3: Fn(&T) -> Option<String>,
{
ListItems {
codemap: codemap,
codemap,
inner: inner.peekable(),
get_lo: get_lo,
get_hi: get_hi,
get_item_string: get_item_string,
prev_span_end: prev_span_end,
next_span_start: next_span_start,
terminator: terminator,
separator: separator,
leave_last: leave_last,
get_lo,
get_hi,
get_item_string,
prev_span_end,
next_span_start,
terminator,
separator,
leave_last,
}
}
@ -841,7 +753,7 @@ pub fn struct_lit_formatting<'a>(
let ends_with_newline = context.config.indent_style() != IndentStyle::Visual
&& tactic == DefinitiveListTactic::Vertical;
ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if force_no_trailing_comma {
SeparatorTactic::Never
@ -849,8 +761,8 @@ pub fn struct_lit_formatting<'a>(
context.config.trailing_comma()
},
separator_place: SeparatorPlace::Back,
shape: shape,
ends_with_newline: ends_with_newline,
shape,
ends_with_newline,
preserve_newline: true,
config: context.config,
}

View File

@ -20,6 +20,8 @@
// and those with brackets will be formatted as array literals.
use std::collections::HashMap;
use config::lists::*;
use syntax::ast;
use syntax::codemap::{BytePos, Span};
use syntax::parse::new_parser_from_tts;
@ -33,6 +35,7 @@ use syntax::util::ThinVec;
use codemap::SpanUtils;
use comment::{contains_comment, remove_trailing_white_spaces, FindUncommented};
use expr::{rewrite_array, rewrite_call_inner};
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use shape::{Indent, Shape};
use utils::{format_visibility, mk_sp};
@ -101,7 +104,7 @@ fn parse_macro_arg(parser: &mut Parser) -> Option<MacroArg> {
parser.sess.span_diagnostic.reset_err_count();
}
}
}
};
}
parse_macro_arg!(Expr, parse_expr);
@ -283,6 +286,7 @@ pub fn rewrite_macro(
pub fn rewrite_macro_def(
context: &RewriteContext,
shape: Shape,
indent: Indent,
def: &ast::MacroDef,
ident: ast::Ident,
@ -291,85 +295,67 @@ pub fn rewrite_macro_def(
) -> Option<String> {
let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
if def.legacy {
return snippet;
}
let mut parser = MacroParser::new(def.stream().into_trees());
let mut parsed_def = match parser.parse() {
let parsed_def = match parser.parse() {
Some(def) => def,
None => return snippet,
};
// Only attempt to format function-like macros.
if parsed_def.branches.len() != 1 || parsed_def.branches[0].args_paren_kind != DelimToken::Paren
{
// FIXME(#1539): implement for non-sugared macros.
return snippet;
}
let branch = parsed_def.branches.remove(0);
let args_str = format_macro_args(branch.args)?;
// The macro body is the most interesting part. It might end up as various
// AST nodes, but also has special variables (e.g, `$foo`) which can't be
// parsed as regular Rust code (and note that these can be escaped using
// `$$`). We'll try and format like an AST node, but we'll substitute
// variables for new names with the same length first.
let old_body = context.snippet(branch.body).trim();
let (body_str, substs) = replace_names(old_body);
// We'll hack the indent below, take this into account when formatting,
let mut config = context.config.clone();
let new_width = config.max_width() - indent.block_indent(&config).width();
config.set().max_width(new_width);
config.set().hide_parse_errors(true);
// First try to format as items, then as statements.
let new_body = match ::format_snippet(&body_str, &config) {
Some(new_body) => new_body,
None => match ::format_code_block(&body_str, &config) {
Some(new_body) => new_body,
None => return snippet,
},
let mut result = if def.legacy {
String::from("macro_rules!")
} else {
format!("{}macro", format_visibility(vis))
};
// Indent the body since it is in a block.
let indent_str = indent.block_indent(&config).to_string(&config);
let mut new_body = new_body
.lines()
.map(|l| {
if l.is_empty() {
l.to_owned()
} else {
format!("{}{}", indent_str, l)
}
})
.collect::<Vec<_>>()
.join("\n");
result += " ";
result += &ident.name.as_str();
// Undo our replacement of macro variables.
// FIXME: this could be *much* more efficient.
for (old, new) in substs.iter() {
if old_body.find(new).is_some() {
debug!(
"rewrite_macro_def: bailing matching variable: `{}` in `{}`",
new, ident
);
return snippet;
}
new_body = new_body.replace(new, old);
let multi_branch_style = def.legacy || parsed_def.branches.len() != 1;
let arm_shape = if multi_branch_style {
shape
.block_indent(context.config.tab_spaces())
.with_max_width(context.config)
} else {
shape
};
let branch_items = itemize_list(
context.codemap,
parsed_def.branches.iter(),
"}",
";",
|branch| branch.span.lo(),
|branch| branch.span.hi(),
|branch| branch.rewrite(context, arm_shape, multi_branch_style),
context.codemap.span_after(span, "{"),
span.hi(),
false,
).collect::<Vec<_>>();
let fmt = ListFormatting {
tactic: DefinitiveListTactic::Vertical,
separator: if def.legacy { ";" } else { "" },
trailing_separator: SeparatorTactic::Always,
separator_place: SeparatorPlace::Back,
shape: arm_shape,
ends_with_newline: true,
preserve_newline: true,
config: context.config,
};
if multi_branch_style {
result += " {\n";
result += &arm_shape.indent.to_string(context.config);
}
let result = format!(
"{}macro {}({}) {{\n{}\n{}}}",
format_visibility(vis),
ident,
args_str,
new_body,
indent.to_string(&context.config),
);
result += write_list(&branch_items, &fmt)?.as_str();
if multi_branch_style {
result += "\n";
result += &indent.to_string(context.config);
result += "}";
}
Some(result)
}
@ -377,7 +363,7 @@ pub fn rewrite_macro_def(
// Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we
// aren't causing problems.
// This should also work for escaped `$` variables, where we leave earlier `$`s.
fn replace_names(input: &str) -> (String, HashMap<String, String>) {
fn replace_names(input: &str) -> Option<(String, HashMap<String, String>)> {
// Each substitution will require five or six extra bytes.
let mut result = String::with_capacity(input.len() + 64);
let mut substs = HashMap::new();
@ -409,6 +395,9 @@ fn replace_names(input: &str) -> (String, HashMap<String, String>) {
dollar_count = 0;
cur_name = String::new();
} else if c == '(' && cur_name.is_empty() {
// FIXME: Support macro def with repeat.
return None;
} else if c.is_alphanumeric() {
cur_name.push(c);
}
@ -433,7 +422,7 @@ fn replace_names(input: &str) -> (String, HashMap<String, String>) {
debug!("replace_names `{}` {:?}", result, substs);
(result, substs)
Some((result, substs))
}
// This is a bit sketchy. The token rules probably need tweaking, but it works
@ -467,13 +456,10 @@ fn format_macro_args(toks: ThinTokenStream) -> Option<String> {
insert_space = next_space(&t);
}
TokenTree::Delimited(_, d) => {
let formatted = format_macro_args(d.tts)?;
match insert_space {
SpaceState::Always => {
result.push(' ');
}
_ => {}
if let SpaceState::Always = insert_space {
result.push(' ');
}
let formatted = format_macro_args(d.tts)?;
match d.delim {
DelimToken::Paren => {
result.push_str(&format!("({})", formatted));
@ -711,24 +697,34 @@ impl MacroParser {
// `(` ... `)` `=>` `{` ... `}`
fn parse_branch(&mut self) -> Option<MacroBranch> {
let (args_paren_kind, args) = match self.toks.next()? {
let tok = self.toks.next()?;
let (lo, args_paren_kind) = match tok {
TokenTree::Token(..) => return None,
TokenTree::Delimited(_, ref d) => (d.delim, d.tts.clone().into()),
TokenTree::Delimited(sp, ref d) => (sp.lo(), d.delim),
};
let args = tok.joint().into();
match self.toks.next()? {
TokenTree::Token(_, Token::FatArrow) => {}
_ => return None,
}
let body = match self.toks.next()? {
let (mut hi, body) = match self.toks.next()? {
TokenTree::Token(..) => return None,
TokenTree::Delimited(sp, _) => {
let data = sp.data();
Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt)
(
data.hi,
Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt),
)
}
};
if let Some(TokenTree::Token(sp, Token::Semi)) = self.toks.look_ahead(0) {
self.toks.next();
hi = sp.hi();
}
Some(MacroBranch {
args,
span: mk_sp(lo, hi),
args_paren_kind,
args,
body,
})
}
@ -742,11 +738,102 @@ struct Macro {
// FIXME: it would be more efficient to use references to the token streams
// rather than clone them, if we can make the borrowing work out.
struct MacroBranch {
args: ThinTokenStream,
span: Span,
args_paren_kind: DelimToken,
args: ThinTokenStream,
body: Span,
}
impl MacroBranch {
fn rewrite(
&self,
context: &RewriteContext,
shape: Shape,
multi_branch_style: bool,
) -> Option<String> {
// Only attempt to format function-like macros.
if self.args_paren_kind != DelimToken::Paren {
// FIXME(#1539): implement for non-sugared macros.
return None;
}
let mut result = format_macro_args(self.args.clone())?;
if multi_branch_style {
result += " =>";
}
// The macro body is the most interesting part. It might end up as various
// AST nodes, but also has special variables (e.g, `$foo`) which can't be
// parsed as regular Rust code (and note that these can be escaped using
// `$$`). We'll try and format like an AST node, but we'll substitute
// variables for new names with the same length first.
let old_body = context.snippet(self.body).trim();
let (body_str, substs) = replace_names(old_body)?;
let mut config = context.config.clone();
config.set().hide_parse_errors(true);
result += " {";
let has_block_body = old_body.starts_with('{');
let body_indent = if has_block_body {
shape.indent
} else {
// We'll hack the indent below, take this into account when formatting,
let body_indent = shape.indent.block_indent(&config);
let new_width = config.max_width() - body_indent.width();
config.set().max_width(new_width);
body_indent
};
// First try to format as items, then as statements.
let new_body = match ::format_snippet(&body_str, &config) {
Some(new_body) => new_body,
None => match ::format_code_block(&body_str, &config) {
Some(new_body) => new_body,
None => return None,
},
};
// Indent the body since it is in a block.
let indent_str = body_indent.to_string(&config);
let mut new_body = new_body
.trim_right()
.lines()
.fold(String::new(), |mut s, l| {
if !l.is_empty() {
s += &indent_str;
}
s + l + "\n"
});
// Undo our replacement of macro variables.
// FIXME: this could be *much* more efficient.
for (old, new) in &substs {
if old_body.find(new).is_some() {
debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
return None;
}
new_body = new_body.replace(new, old);
}
if has_block_body {
result += new_body.trim();
} else if !new_body.is_empty() {
result += "\n";
result += &new_body;
result += &shape.indent.to_string(&config);
}
result += "}";
Some(result)
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use config::lists::*;
use syntax::ast::{self, BindingMode, FieldPat, Pat, PatKind, RangeEnd, RangeSyntax};
use syntax::codemap::{self, BytePos, Span};
use syntax::ptr;
@ -17,7 +18,7 @@ use comment::FindUncommented;
use expr::{can_be_overflowed_expr, rewrite_call_inner, rewrite_pair, rewrite_unary_prefix,
wrap_struct_field, PairParts};
use lists::{itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
struct_lit_tactic, write_list, DefinitiveListTactic, SeparatorPlace, SeparatorTactic};
struct_lit_tactic, write_list};
use macros::{rewrite_macro, MacroPosition};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
@ -65,10 +66,15 @@ impl Rewrite for Pat {
RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
RangeEnd::Excluded => "..",
};
let infix = if context.config.spaces_around_ranges() {
format!(" {} ", infix)
} else {
infix.to_owned()
};
rewrite_pair(
&**lhs,
&**rhs,
PairParts::new("", infix, ""),
PairParts::new("", &infix, ""),
context,
shape,
SeparatorPlace::Front,

View File

@ -36,8 +36,8 @@ pub struct Mismatch {
impl Mismatch {
fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
Mismatch {
line_number: line_number,
line_number_orig: line_number_orig,
line_number,
line_number_orig,
lines: Vec::new(),
}
}

View File

@ -29,8 +29,8 @@ const INDENT_BUFFER: &str =
impl Indent {
pub fn new(block_indent: usize, alignment: usize) -> Indent {
Indent {
block_indent: block_indent,
alignment: alignment,
block_indent,
alignment,
}
}
@ -161,8 +161,8 @@ impl Shape {
// |<--->| width
pub fn legacy(width: usize, indent: Indent) -> Shape {
Shape {
width: width,
indent: indent,
width,
indent,
offset: indent.alignment,
}
}
@ -170,7 +170,7 @@ impl Shape {
pub fn indented(indent: Indent, config: &Config) -> Shape {
Shape {
width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
indent: indent,
indent,
offset: indent.alignment,
}
}
@ -187,9 +187,9 @@ impl Shape {
pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
Shape {
width: width,
indent: indent,
offset: offset,
width,
indent,
offset,
}
}

View File

@ -20,32 +20,30 @@ pub trait Spanned {
}
macro_rules! span_with_attrs_lo_hi {
($this:ident, $lo:expr, $hi:expr) => {
{
let attrs = outer_attributes(&$this.attrs);
if attrs.is_empty() {
mk_sp($lo, $hi)
} else {
mk_sp(attrs[0].span.lo(), $hi)
}
($this: ident, $lo: expr, $hi: expr) => {{
let attrs = outer_attributes(&$this.attrs);
if attrs.is_empty() {
mk_sp($lo, $hi)
} else {
mk_sp(attrs[0].span.lo(), $hi)
}
}
}};
}
macro_rules! span_with_attrs {
($this:ident) => {
($this: ident) => {
span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi())
}
};
}
macro_rules! implement_spanned {
($this:ty) => {
($this: ty) => {
impl Spanned for $this {
fn span(&self) -> Span {
span_with_attrs!(self)
}
}
}
};
}
// Implement `Spanned` for structs with `attrs` field.

View File

@ -36,9 +36,9 @@ impl<'a> StringFormat<'a> {
closer: "\"",
line_start: " ",
line_end: "\\",
shape: shape,
shape,
trim_end: false,
config: config,
config,
}
}
}

View File

@ -11,6 +11,7 @@
use std::iter::ExactSizeIterator;
use std::ops::Deref;
use config::lists::*;
use syntax::ast::{self, FunctionRetTy, Mutability};
use syntax::codemap::{self, BytePos, Span};
use syntax::print::pprust;
@ -20,8 +21,7 @@ use codemap::SpanUtils;
use config::{IndentStyle, TypeDensity};
use expr::{rewrite_pair, rewrite_tuple, rewrite_unary_prefix, wrap_args_with_parens, PairParts};
use items::{format_generics_item_list, generics_shape_from_config};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListTactic, Separator,
SeparatorPlace, SeparatorTactic};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use macros::{rewrite_macro, MacroPosition};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
@ -352,7 +352,7 @@ where
);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: if !context.use_block_indent() || variadic {
SeparatorTactic::Never

View File

@ -178,7 +178,7 @@ pub fn last_line_extendable(s: &str) -> bool {
}
for c in s.chars().rev() {
match c {
')' | ']' | '}' | '?' | '>' => continue,
'(' | ')' | ']' | '}' | '?' | '>' => continue,
'\n' => break,
_ if c.is_whitespace() => continue,
_ => return false,
@ -254,82 +254,6 @@ pub fn count_newlines(input: &str) -> usize {
input.chars().filter(|&c| c == '\n').count()
}
// Macro for deriving implementations of Serialize/Deserialize for enums
#[macro_export]
macro_rules! impl_enum_serialize_and_deserialize {
( $e:ident, $( $x:ident ),* ) => {
impl ::serde::ser::Serialize for $e {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ::serde::ser::Serializer
{
use serde::ser::Error;
// We don't know whether the user of the macro has given us all options.
#[allow(unreachable_patterns)]
match *self {
$(
$e::$x => serializer.serialize_str(stringify!($x)),
)*
_ => {
Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
}
}
}
}
impl<'de> ::serde::de::Deserialize<'de> for $e {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where D: ::serde::Deserializer<'de> {
use serde::de::{Error, Visitor};
use std::marker::PhantomData;
use std::fmt;
struct StringOnly<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for StringOnly<T>
where T: ::serde::Deserializer<'de> {
type Value = String;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string")
}
fn visit_str<E>(self, value: &str) -> Result<String, E> {
Ok(String::from(value))
}
}
let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
$(
if stringify!($x).eq_ignore_ascii_case(&s) {
return Ok($e::$x);
}
)*
static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
Err(D::Error::unknown_variant(&s, ALLOWED))
}
}
impl ::std::str::FromStr for $e {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$(
if stringify!($x).eq_ignore_ascii_case(s) {
return Ok($e::$x);
}
)*
Err("Bad variant")
}
}
impl ::config::ConfigType for $e {
fn doc_hint() -> String {
let mut variants = Vec::new();
$(
variants.push(stringify!($x));
)*
format!("[{}]", variants.join("|"))
}
}
};
}
macro_rules! msg {
($($arg:tt)*) => (
match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
@ -342,9 +266,9 @@ macro_rules! msg {
// For format_missing and last_pos, need to use the source callsite (if applicable).
// Required as generated code spans aren't guaranteed to follow on from the last span.
macro_rules! source {
($this:ident, $sp: expr) => {
($this: ident, $sp: expr) => {
$sp.source_callsite()
}
};
}
pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
@ -353,28 +277,29 @@ pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
// Return true if the given span does not intersect with file lines.
macro_rules! out_of_file_lines_range {
($self:ident, $span:expr) => {
!$self.config
($self: ident, $span: expr) => {
!$self
.config
.file_lines()
.intersects(&$self.codemap.lookup_line_range($span))
}
};
}
macro_rules! skip_out_of_file_lines_range {
($self:ident, $span:expr) => {
($self: ident, $span: expr) => {
if out_of_file_lines_range!($self, $span) {
return None;
}
}
};
}
macro_rules! skip_out_of_file_lines_range_visitor {
($self:ident, $span:expr) => {
($self: ident, $span: expr) => {
if out_of_file_lines_range!($self, $span) {
$self.push_rewrite($span, None);
return;
}
}
};
}
// Wraps String in an Option. Returns Some when the string adheres to the

View File

@ -12,6 +12,7 @@
use std::cmp;
use config::lists::*;
use syntax::ast;
use syntax::codemap::{BytePos, Span};
@ -19,8 +20,7 @@ use codemap::SpanUtils;
use comment::{combine_strs_with_missing_comments, contains_comment};
use expr::rewrite_field;
use items::{rewrite_struct_field, rewrite_struct_field_prefix};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListTactic, Separator,
SeparatorPlace};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use rewrite::{Rewrite, RewriteContext};
use shape::{Indent, Shape};
use spanned::Spanned;
@ -247,7 +247,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
);
let fmt = ListFormatting {
tactic: tactic,
tactic,
separator: ",",
trailing_separator: context.config.trailing_comma(),
separator_place: SeparatorPlace::Back,

View File

@ -10,8 +10,9 @@
use std::cmp;
use config::lists::*;
use syntax::{ast, visit};
use syntax::attr::HasAttrs;
use syntax::attr::{self, HasAttrs};
use syntax::codemap::{self, BytePos, CodeMap, Pos, Span};
use syntax::parse::ParseSess;
@ -23,8 +24,7 @@ use config::{BraceStyle, Config};
use expr::rewrite_literal;
use items::{format_impl, format_trait, format_trait_alias, rewrite_associated_impl_type,
rewrite_associated_type, rewrite_type_alias, FnSig, StaticParts, StructParts};
use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace,
SeparatorTactic};
use lists::{itemize_list, write_list, ListFormatting};
use macros::{rewrite_macro, rewrite_macro_def, MacroPosition};
use regex::Regex;
use rewrite::{Rewrite, RewriteContext};
@ -32,6 +32,34 @@ use shape::{Indent, Shape};
use spanned::Spanned;
use utils::{self, contains_skip, count_newlines, inner_attributes, mk_sp, ptr_vec_to_ref_vec};
/// Returns attributes that are within `outer_span`.
pub fn filter_inline_attrs(attrs: &[ast::Attribute], outer_span: Span) -> Vec<ast::Attribute> {
attrs
.iter()
.filter(|a| outer_span.lo() <= a.span.lo() && a.span.hi() <= outer_span.hi())
.cloned()
.collect()
}
/// Returns true for `mod foo;`, false for `mod foo { .. }`.
fn is_mod_decl(item: &ast::Item) -> bool {
match item.node {
ast::ItemKind::Mod(ref m) => m.inner.hi() != item.span.hi(),
_ => false,
}
}
fn contains_macro_use_attr(attrs: &[ast::Attribute], span: Span) -> bool {
attr::contains_name(&filter_inline_attrs(attrs, span), "macro_use")
}
/// Returns true for `mod foo;` without any inline attributes.
/// We cannot reorder modules with attributes because doing so can break the code.
/// e.g. `#[macro_use]`.
fn is_mod_decl_without_attr(item: &ast::Item) -> bool {
is_mod_decl(item) && !contains_macro_use_attr(&item.attrs, item.span())
}
fn is_use_item(item: &ast::Item) -> bool {
match item.node {
ast::ItemKind::Use(_) => true,
@ -39,6 +67,10 @@ fn is_use_item(item: &ast::Item) -> bool {
}
}
fn is_use_item_without_attr(item: &ast::Item) -> bool {
is_use_item(item) && !contains_macro_use_attr(&item.attrs, item.span())
}
fn is_extern_crate(item: &ast::Item) -> bool {
match item.node {
ast::ItemKind::ExternCrate(..) => true,
@ -46,6 +78,10 @@ fn is_extern_crate(item: &ast::Item) -> bool {
}
}
fn is_extern_crate_without_attr(item: &ast::Item) -> bool {
is_extern_crate(item) && !contains_macro_use_attr(&item.attrs, item.span())
}
/// Creates a string slice corresponding to the specified span.
pub struct SnippetProvider<'a> {
/// A pointer to the content of the file we are formatting.
@ -318,39 +354,26 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let filtered_attrs;
let mut attrs = &item.attrs;
match item.node {
ast::ItemKind::Mod(ref m) => {
let outer_file = self.codemap.lookup_char_pos(item.span.lo()).file;
let inner_file = self.codemap.lookup_char_pos(m.inner.lo()).file;
if outer_file.name == inner_file.name {
// Module is inline, in this case we treat modules like any
// other item.
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
self.push_skipped_with_span(item.span());
return;
}
} else if contains_skip(&item.attrs) {
// Module is not inline, but should be skipped.
// Module is inline, in this case we treat it like any other item.
_ if !is_mod_decl(item) => {
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
self.push_skipped_with_span(item.span());
return;
} else {
// Module is not inline and should not be skipped. We want
// to process only the attributes in the current file.
filtered_attrs = item.attrs
.iter()
.filter_map(|a| {
let attr_file = self.codemap.lookup_char_pos(a.span.lo()).file;
if attr_file.name == outer_file.name {
Some(a.clone())
} else {
None
}
})
.collect::<Vec<_>>();
// Assert because if we should skip it should be caught by
// the above case.
assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
attrs = &filtered_attrs;
}
}
// Module is not inline, but should be skipped.
ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => {
return;
}
// Module is not inline and should not be skipped. We want
// to process only the attributes in the current file.
ast::ItemKind::Mod(..) => {
filtered_attrs = filter_inline_attrs(&item.attrs, item.span());
// Assert because if we should skip it should be caught by
// the above case.
assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
attrs = &filtered_attrs;
}
_ => {
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
self.push_skipped_with_span(item.span());
@ -397,8 +420,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
self.last_pos = source!(self, item.span).hi();
}
ast::ItemKind::Mod(ref module) => {
let is_inline = !is_mod_decl(item);
self.format_missing_with_indent(source!(self, item.span).lo());
self.format_mod(module, &item.vis, item.span, item.ident, attrs);
self.format_mod(module, &item.vis, item.span, item.ident, attrs, is_inline);
}
ast::ItemKind::Mac(ref mac) => {
self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
@ -439,6 +463,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
ast::ItemKind::MacroDef(ref def) => {
let rewrite = rewrite_macro_def(
&self.get_context(),
self.shape(),
self.block_indent,
def,
item.ident,
@ -576,14 +601,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
snippet_provider: &'a SnippetProvider,
) -> FmtVisitor<'a> {
FmtVisitor {
parse_session: parse_session,
parse_session,
codemap: parse_session.codemap(),
buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
last_pos: BytePos(0),
block_indent: Indent::empty(),
config: config,
config,
is_if_else_block: false,
snippet_provider: snippet_provider,
snippet_provider,
line_number: 0,
skipped_range: vec![],
}
@ -649,34 +674,39 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
}
fn walk_items(&mut self, mut items_left: &[&ast::Item]) {
while !items_left.is_empty() {
// If the next item is a `use` declaration, then extract it and any subsequent `use`s
// to be potentially reordered within `format_imports`. Otherwise, just format the
// next item for output.
if self.config.reorder_imports() && is_use_item(&*items_left[0]) {
let used_items_len = self.reorder_items(
items_left,
&is_use_item,
self.config.reorder_imports_in_group(),
);
macro try_reorder_items_with($reorder: ident, $in_group: ident, $pred: ident) {
if self.config.$reorder() && $pred(&*items_left[0]) {
let used_items_len =
self.reorder_items(items_left, &$pred, self.config.$in_group());
let (_, rest) = items_left.split_at(used_items_len);
items_left = rest;
} else if self.config.reorder_extern_crates() && is_extern_crate(&*items_left[0]) {
let used_items_len = self.reorder_items(
items_left,
&is_extern_crate,
self.config.reorder_extern_crates_in_group(),
);
let (_, rest) = items_left.split_at(used_items_len);
items_left = rest;
} else {
// `unwrap()` is safe here because we know `items_left`
// has elements from the loop condition
let (item, rest) = items_left.split_first().unwrap();
self.visit_item(item);
items_left = rest;
continue;
}
}
while !items_left.is_empty() {
// If the next item is a `use`, `extern crate` or `mod`, then extract it and any
// subsequent items that have the same item kind to be reordered within
// `format_imports`. Otherwise, just format the next item for output.
{
try_reorder_items_with!(
reorder_imports,
reorder_imports_in_group,
is_use_item_without_attr
);
try_reorder_items_with!(
reorder_extern_crates,
reorder_extern_crates_in_group,
is_extern_crate_without_attr
);
try_reorder_items_with!(reorder_modules, reorder_modules, is_mod_decl_without_attr);
}
// Reaching here means items were not reordered. There must be at least
// one item left in `items_left`, so calling `unwrap()` here is safe.
let (item, rest) = items_left.split_first().unwrap();
self.visit_item(item);
items_left = rest;
}
}
fn walk_mod_items(&mut self, m: &ast::Mod) {
@ -722,13 +752,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
s: Span,
ident: ast::Ident,
attrs: &[ast::Attribute],
is_internal: bool,
) {
// Decide whether this is an inline mod or an external mod.
let local_file_name = self.codemap.span_to_filename(s);
let inner_span = source!(self, m.inner);
let is_internal = !(inner_span.lo().0 == 0 && inner_span.hi().0 == 0)
&& local_file_name == self.codemap.span_to_filename(inner_span);
self.push_str(&*utils::format_visibility(vis));
self.push_str("mod ");
self.push_str(&ident.to_string());

View File

@ -8,14 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate regex;
extern crate rustfmt_nightly as rustfmt;
extern crate rustfmt_config as config;
extern crate rustfmt_core as rustfmt;
extern crate term;
use std::collections::HashMap;
@ -26,8 +25,9 @@ use std::path::{Path, PathBuf};
use std::str::Chars;
use rustfmt::*;
use rustfmt::config::{Color, Config, ReportTactic};
use rustfmt::filemap::{write_system_newlines, FileMap};
use config::{Color, Config, ReportTactic};
use config::summary::Summary;
use rustfmt::filemap::write_system_newlines;
use rustfmt::rustfmt_diff::*;
const DIFF_CONTEXT_SIZE: usize = 3;
@ -95,7 +95,7 @@ fn verify_config_test_names() {
let config_name = path.file_name().unwrap().to_str().unwrap();
// Make sure that config name is used in the files in the directory.
verify_config_used(&path, &config_name);
verify_config_used(&path, config_name);
}
}
}
@ -105,7 +105,7 @@ fn verify_config_test_names() {
// println!) that is used by `rustfmt::rustfmt_diff::print_diff`. Writing
// using only one or the other will cause the output order to differ when
// `print_diff` selects the approach not used.
fn write_message(msg: String) {
fn write_message(msg: &str) {
let mut writer = OutputWriter::new(Color::Auto);
writer.writeln(&format!("{}", msg), None);
}
@ -210,10 +210,26 @@ fn idempotence_tests() {
// no warnings are emitted.
#[test]
fn self_tests() {
let mut files = get_test_files(Path::new("src/bin"), false);
files.append(&mut get_test_files(Path::new("tests"), false));
files.push(PathBuf::from("src/lib.rs"));
files.push(PathBuf::from("build.rs"));
let mut files = get_test_files(Path::new("tests"), false);
let bin_directories = vec![
"cargo-fmt",
"git-rustfmt",
"rustfmt-bin",
"rustfmt-format-diff",
];
for dir in bin_directories {
let mut path = PathBuf::from("..");
path.push(dir);
path.push("src/main.rs");
files.push(path);
}
let lib_directories = vec!["rustfmt-core", "rustfmt-config"];
for dir in lib_directories {
let mut path = PathBuf::from("..");
path.push(dir);
path.push("src/lib.rs");
files.push(path);
}
let (reports, count, fails) = check_files(files);
let mut warnings = 0;
@ -285,6 +301,16 @@ fn format_lines_errors_are_reported() {
assert!(error_summary.has_formatting_errors());
}
#[test]
fn format_lines_errors_are_reported_with_tabs() {
let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap();
let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier));
let config = Config::from_toml("hard_tabs = true").unwrap();
let (error_summary, _file_map, _report) =
format_input::<io::Stdout>(input, &config, None).unwrap();
assert!(error_summary.has_formatting_errors());
}
// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files(files: Vec<PathBuf>) -> (Vec<FormatReport>, u32, u32) {
@ -373,8 +399,8 @@ pub enum IdempotentCheckError {
}
pub fn idempotent_check(filename: &PathBuf) -> Result<FormatReport, IdempotentCheckError> {
let sig_comments = read_significant_comments(&filename);
let config = read_config(&filename);
let sig_comments = read_significant_comments(filename);
let config = read_config(filename);
let (error_summary, file_map, format_report) = format_file(filename, &config);
if error_summary.has_parsing_errors() {
return Err(IdempotentCheckError::Parse);
@ -663,10 +689,12 @@ impl ConfigCodeBlock {
fn get_block_config(&self) -> Config {
let mut config = Config::default();
config.override_value(
self.config_name.as_ref().unwrap(),
self.config_value.as_ref().unwrap(),
);
if self.config_value.is_some() && self.config_value.is_some() {
config.override_value(
self.config_name.as_ref().unwrap(),
self.config_value.as_ref().unwrap(),
);
}
config
}
@ -674,16 +702,24 @@ impl ConfigCodeBlock {
// We never expect to not have a code block.
assert!(self.code_block.is_some() && self.code_block_start.is_some());
if self.config_name.is_none() {
write_message(format!(
// See if code block begins with #![rustfmt_skip].
let fmt_skip = self.code_block
.as_ref()
.unwrap()
.split("\n")
.nth(0)
.unwrap_or("") == "#![rustfmt_skip]";
if self.config_name.is_none() && !fmt_skip {
write_message(&format!(
"No configuration name for {}:{}",
CONFIGURATIONS_FILE_NAME,
self.code_block_start.unwrap()
));
return false;
}
if self.config_value.is_none() {
write_message(format!(
if self.config_value.is_none() && !fmt_skip {
write_message(&format!(
"No configuration value for {}:{}",
CONFIGURATIONS_FILE_NAME,
self.code_block_start.unwrap()
@ -695,7 +731,7 @@ impl ConfigCodeBlock {
fn has_parsing_errors(&self, error_summary: Summary) -> bool {
if error_summary.has_parsing_errors() {
write_message(format!(
write_message(&format!(
"\u{261d}\u{1f3fd} Cannot format {}:{}",
CONFIGURATIONS_FILE_NAME,
self.code_block_start.unwrap()
@ -718,7 +754,7 @@ impl ConfigCodeBlock {
});
}
fn formatted_has_diff(&self, file_map: FileMap) -> bool {
fn formatted_has_diff(&self, file_map: &FileMap) -> bool {
let &(ref _file_name, ref text) = file_map.first().unwrap();
let compare = make_diff(self.code_block.as_ref().unwrap(), text, DIFF_CONTEXT_SIZE);
if !compare.is_empty() {
@ -744,14 +780,14 @@ impl ConfigCodeBlock {
let (error_summary, file_map, _report) =
format_input::<io::Stdout>(input, &config, None).unwrap();
!self.has_parsing_errors(error_summary) && !self.formatted_has_diff(file_map)
!self.has_parsing_errors(error_summary) && !self.formatted_has_diff(&file_map)
}
// Extract a code block from the iterator. Behavior:
// - Rust code blocks are identifed by lines beginning with "```rust".
// - One explicit configuration setting is supported per code block.
// - Rust code blocks with no configuration setting are illegal and cause an
// assertion failure.
// assertion failure, unless the snippet begins with #![rustfmt_skip].
// - Configuration names in Configurations.md must be in the form of
// "## `NAME`".
// - Configuration values in Configurations.md must be in the form of
@ -761,7 +797,7 @@ impl ConfigCodeBlock {
prev: Option<&ConfigCodeBlock>,
) -> Option<ConfigCodeBlock> {
let mut code_block = ConfigCodeBlock::new();
code_block.config_name = prev.map_or(None, |cb| cb.config_name.clone());
code_block.config_name = prev.and_then(|cb| cb.config_name.clone());
loop {
match ConfigurationSection::get_section(file) {
@ -790,7 +826,7 @@ fn configuration_snippet_tests() {
// entry for each Rust code block found.
fn get_code_blocks() -> Vec<ConfigCodeBlock> {
let mut file_iter = BufReader::new(
fs::File::open(CONFIGURATIONS_FILE_NAME)
fs::File::open(Path::new("..").join(CONFIGURATIONS_FILE_NAME))
.expect(&format!("Couldn't read file {}", CONFIGURATIONS_FILE_NAME)),
).lines()
.map(|l| l.unwrap())

View File

@ -214,3 +214,18 @@ impl Foo {
}).collect();
}
}
// #2415
// Avoid orphan in chain
fn issue2415() {
let base_url = (|| {
// stuff
Ok((|| {
// stuff
Some(value.to_string())
})()
.ok_or("")?)
})()
.unwrap_or_else(|_: Box<::std::error::Error>| String::from(""));
}

Some files were not shown because too many files have changed in this diff Show More