Merge commit '1d8491b120223272b13451fc81265aa64f7f4d5b' into sync-from-rustfmt

This commit is contained in:
Caleb Cartwright 2023-01-24 14:16:03 -06:00
commit 8e1e67dbaa
108 changed files with 2591 additions and 187 deletions

View file

@ -0,0 +1,33 @@
name: Diff Check
on:
workflow_dispatch:
inputs:
clone_url:
description: 'Git url of a rustfmt fork to compare against the latest master rustfmt'
required: true
branch_name:
description: 'Name of the feature branch on the forked repo'
required: true
commit_hash:
description: 'Optional commit hash from the feature branch'
required: false
rustfmt_configs:
description: 'Optional comma separated list of rustfmt config options to pass when running the feature branch'
required: false
jobs:
diff_check:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: install rustup
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
sh rustup-init.sh -y --default-toolchain none
rustup target add x86_64-unknown-linux-gnu
- name: check diff
run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }}

View file

@ -27,7 +27,6 @@ jobs:
tempdir,
futures-rs,
rust-clippy,
failure,
]
include:
# Allowed Failures
@ -63,9 +62,6 @@ jobs:
# Original comment was: temporal build failure due to breaking changes in the nightly compiler
- integration: rust-semverver
allow-failure: true
# Can be moved back to include section after https://github.com/rust-lang-nursery/failure/pull/298 is merged
- integration: failure
allow-failure: true
steps:
- name: checkout

View file

@ -2,6 +2,32 @@
## [Unreleased]
## [1.5.2] 2023-01-24
### Fixed
- Resolve issue when comments are found within const generic defaults in unit structs [#5668](https://github.com/rust-lang/rustfmt/issues/5668)
- Resolve issue when block comments are found within trait generics [#5358](https://github.com/rust-lang/rustfmt/issues/5358)
- Correctly handle alignment of comments containing unicode characters [#5504](https://github.com/rust-lang/rustfmt/issues/5504)
- Properly indent a single generic bound that requires being written across multiple lines [#4689](https://github.com/rust-lang/rustfmt/issues/4689) (n.b. this change is version gated and will only appear when the `version` configuration option is set to `Two`)
### Changed
- Renamed `fn_args_layout` configuration option to `fn_params_layout` [#4149](https://github.com/rust-lang/rustfmt/issues/4149). Note that `fn_args_layout` has only been soft deprecated: `fn_args_layout` will continue to work without issue, but rustfmt will display a warning to encourage users to switch to the new name
### Added
- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726)
### Misc
- rustfmt now internally supports the ability to have both stable and unstable variants of a configuration option [#5378](https://github.com/rust-lang/rustfmt/issues/5378). This ability will allow the rustfmt team to make certain configuration options available on stable toolchains more quickly because we no longer have to wait for _every_ variant to be stable-ready before stabilizing _any_ variant.
### Install/Download Options
- **rustup (nightly)** - nightly-2023-01-24
- **GitHub Release Binaries** - [Release v1.5.2](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.2)
- **Build from source** - [Tag v1.5.2](https://github.com/rust-lang/rustfmt/tree/v1.5.2), see instructions for how to [install rustfmt from source][install-from-source]
## [1.5.1] 2022-06-24
**N.B** A bug was introduced in v1.5.0/nightly-2022-06-15 which modified formatting. If you happened to run rustfmt over your code with one of those ~10 nightlies it's possible you may have seen formatting changes, and you may see additional changes after this fix since that bug has now been reverted.
@ -840,7 +866,7 @@ from formatting an attribute #3665
- Fix formatting of raw string literals #2983
- Handle chain with try operators with spaces #2986
- Use correct shape in Visual tuple rewriting #2987
- Impove formatting of arguments with `visual_style = "Visual"` option #2988
- Improve formatting of arguments with `visual_style = "Visual"` option #2988
- Change `print_diff` to output the correct line number 992b179
- Propagate errors about failing to rewrite a macro 6f318e3
- Handle formatting of long function signature #3010

View file

@ -476,7 +476,7 @@ checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
[[package]]
name = "rustfmt-config_proc_macro"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",
@ -485,7 +485,7 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
version = "1.5.1"
version = "1.5.2"
dependencies = [
"annotate-snippets",
"anyhow",

View file

@ -1,7 +1,7 @@
[package]
name = "rustfmt-nightly"
version = "1.5.1"
version = "1.5.2"
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang/rustfmt"
readme = "README.md"
@ -57,7 +57,7 @@ unicode-segmentation = "1.9"
unicode-width = "0.1"
unicode_categories = "0.1"
rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" }
rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" }
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`

View file

@ -1,6 +1,6 @@
# Configuring Rustfmt
Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/1.0.4/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well.
Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well.
A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this:
@ -425,7 +425,7 @@ fn example() {
## `comment_width`
Maximum length of comments. No effect unless`wrap_comments = true`.
Maximum length of comments. No effect unless `wrap_comments = true`.
- **Default value**: `80`
- **Possible values**: any positive integer
@ -589,7 +589,7 @@ doesn't get ignored when aligning.
#### `0` (default):
```rust
enum Bar {
enum Foo {
A = 0,
Bb = 1,
RandomLongVariantGoesHere = 10,
@ -645,7 +645,8 @@ trailing whitespaces.
## `fn_args_layout`
Control the layout of arguments in a function
This option is deprecated and has been renamed to `fn_params_layout` to better communicate that
it affects the layout of parameters in function signatures.
- **Default value**: `"Tall"`
- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"`
@ -753,6 +754,8 @@ trait Lorem {
}
```
See also [`fn_params_layout`](#fn_params_layout)
## `fn_call_width`
Maximum width of the args of a function call before falling back to vertical formatting.
@ -765,6 +768,117 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi
See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
## `fn_params_layout`
Control the layout of parameters in function signatures.
- **Default value**: `"Tall"`
- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"`
- **Stable**: Yes
#### `"Tall"` (default):
```rust
trait Lorem {
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,
) {
// body
}
}
```
#### `"Compressed"`:
```rust
trait Lorem {
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,
) {
// body
}
}
```
#### `"Vertical"`:
```rust
trait Lorem {
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,
) {
// body
}
}
```
## `fn_single_line`
Put single-expression functions on a single line
@ -1014,6 +1128,62 @@ macro_rules! foo {
See also [`format_macro_matchers`](#format_macro_matchers).
## `skip_macro_invocations`
Skip formatting the bodies of macro invocations with the following names.
rustfmt will not format any macro invocation for macros with names set in this list.
Including the special value "*" will prevent any macro invocations from being formatted.
Note: This option does not have any impact on how rustfmt formats macro definitions.
- **Default value**: `[]`
- **Possible values**: a list of macro name idents, `["name_0", "name_1", ..., "*"]`
- **Stable**: No (tracking issue: [#5346](https://github.com/rust-lang/rustfmt/issues/5346))
#### `[]` (default):
rustfmt will follow its standard approach to formatting macro invocations.
No macro invocations will be skipped based on their name. More information about rustfmt's standard macro invocation formatting behavior can be found in [#5437](https://github.com/rust-lang/rustfmt/discussions/5437).
```rust
lorem!(
const _: u8 = 0;
);
ipsum!(
const _: u8 = 0;
);
```
#### `["lorem"]`:
The named macro invocations will be skipped.
```rust
lorem!(
const _: u8 = 0;
);
ipsum!(
const _: u8 = 0;
);
```
#### `["*"]`:
The special selector `*` will skip all macro invocations.
```rust
lorem!(
const _: u8 = 0;
);
ipsum!(
const _: u8 = 0;
);
```
## `format_strings`
@ -1687,13 +1857,16 @@ pub enum Foo {}
## `imports_granularity`
How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
Controls how imports are structured in `use` statements. Imports will be merged or split to the configured level of granularity.
Similar to other `import` related configuration options, this option operates within the bounds of user-defined groups of imports. See [`group_imports`](#group_imports) for more information on import groups.
Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments.
- **Default value**: `Preserve`
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One`
- **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991))
Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments.
#### `Preserve` (default):

View file

@ -2,7 +2,7 @@ This document outlines processes regarding management of rustfmt.
# Stabilising an Option
In this Section, we describe how to stabilise an option of the rustfmt's configration.
In this Section, we describe how to stabilise an option of the rustfmt's configuration.
## Conditions

View file

@ -1,4 +1,5 @@
set "RUSTFLAGS=-D warnings"
set "RUSTFMT_CI=1"
:: Print version information
rustc -Vv || exit /b 1

View file

@ -3,6 +3,7 @@
set -euo pipefail
export RUSTFLAGS="-D warnings"
export RUSTFMT_CI=1
# Print version information
rustc -Vv

View file

@ -0,0 +1,199 @@
#!/bin/bash
function print_usage() {
echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]"
}
if [ $# -le 1 ]; then
print_usage
exit 1
fi
REMOTE_REPO=$1
FEATURE_BRANCH=$2
OPTIONAL_COMMIT_HASH=$3
OPTIONAL_RUSTFMT_CONFIGS=$4
# OUTPUT array used to collect all the status of running diffs on various repos
STATUSES=()
# Clone a git repository and cd into it.
#
# Parameters:
# $1: git clone url
# $2: directory where the repo should be cloned
function clone_repo() {
GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2
}
# Initialize Git submoduels for the repo.
#
# Parameters
# $1: list of directories to initialize
function init_submodules() {
git submodule update --init $1
}
# Run rusfmt with the --check flag to see if a diff is produced.
#
# Parameters:
# $1: Path to a rustfmt binary
# $2: Output file path for the diff
# $3: Any additional configuration options to pass to rustfmt
#
# Globlas:
# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
function create_diff() {
local config;
if [ -z "$3" ]; then
config="--config=error_on_line_overflow=false,error_on_unformatted=false"
else
config="--config=error_on_line_overflow=false,error_on_unformatted=false,$OPTIONAL_RUSTFMT_CONFIGS"
fi
for i in `find . | grep "\.rs$"`
do
$1 --unstable-features --skip-children --check --color=always $config $i >> $2 2>/dev/null
done
}
# Run the master rustfmt binary and the feature branch binary in the current directory and compare the diffs
#
# Parameters
# $1: Name of the repository (used for logging)
#
# Globlas:
# $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt`
# $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt`
# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
function check_diff() {
echo "running rustfmt (master) on $1"
create_diff $RUSFMT_BIN rustfmt_diff.txt
echo "running rustfmt (feature) on $1"
create_diff $FEATURE_BIN feature_diff.txt $OPTIONAL_RUSTFMT_CONFIGS
echo "checking diff"
local diff;
# we don't add color to the diff since we added color when running rustfmt --check.
# tail -n + 6 removes the git diff header info
# cut -c 2- removes the leading diff characters("+","-"," ") from running git diff.
# Again, the diff output we care about was already added when we ran rustfmt --check
diff=$(
git --no-pager diff --color=never \
--unified=0 --no-index rustfmt_diff.txt feature_diff.txt 2>&1 | tail -n +6 | cut -c 2-
)
if [ -z "$diff" ]; then
echo "no diff detected between rustfmt and the feture branch"
return 0
else
echo "$diff"
return 1
fi
}
# Compiles and produces two rustfmt binaries.
# One for the current master, and another for the feature branch
#
# Parameters:
# $1: Directory where rustfmt will be cloned
#
# Globlas:
# $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test
# $FEATURE_BRANCH: Name of the feature branch
# $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided
function compile_rustfmt() {
RUSTFMT_REPO="https://github.com/rust-lang/rustfmt.git"
clone_repo $RUSTFMT_REPO $1
git remote add feature $REMOTE_REPO
git fetch feature $FEATURE_BRANCH
cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt
if [ -z "$OPTIONAL_COMMIT_HASH" ]; then
git switch $FEATURE_BRANCH
else
git switch $OPTIONAL_COMMIT_HASH --detach
fi
cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt
RUSFMT_BIN=$1/rustfmt
FEATURE_BIN=$1/feature_rustfmt
}
# Check the diff for running rustfmt and the feature branch on all the .rs files in the repo.
#
# Parameters
# $1: Clone URL for the repo
# $2: Name of the repo (mostly used for logging)
# $3: Path to any submodules that should be initialized
function check_repo() {
WORKDIR=$(pwd)
REPO_URL=$1
REPO_NAME=$2
SUBMODULES=$3
local tmp_dir;
tmp_dir=$(mktemp -d -t $REPO_NAME-XXXXXXXX)
clone_repo $REPO_URL $tmp_dir
if [ ! -z "$SUBMODULES" ]; then
init_submodules $SUBMODULES
fi
check_diff $REPO_NAME
# append the status of running `check_diff` to the STATUSES array
STATUSES+=($?)
echo "removing tmp_dir $tmp_dir"
rm -rf $tmp_dir
cd $WORKDIR
}
function main() {
tmp_dir=$(mktemp -d -t rustfmt-XXXXXXXX)
echo Created tmp_dir $tmp_dir
compile_rustfmt $tmp_dir
# run checks
check_repo "https://github.com/rust-lang/rust.git" rust-lang-rust
check_repo "https://github.com/rust-lang/cargo.git" cargo
check_repo "https://github.com/rust-lang/miri.git" miri
check_repo "https://github.com/rust-lang/rust-analyzer.git" rust-analyzer
check_repo "https://github.com/bitflags/bitflags.git" bitflags
check_repo "https://github.com/rust-lang/log.git" log
check_repo "https://github.com/rust-lang/mdBook.git" mdBook
check_repo "https://github.com/rust-lang/packed_simd.git" packed_simd
check_repo "https://github.com/rust-lang/rust-semverver.git" check_repo
check_repo "https://github.com/Stebalien/tempfile.git" tempfile
check_repo "https://github.com/rust-lang/futures-rs.git" futures-rs
check_repo "https://github.com/dtolnay/anyhow.git" anyhow
check_repo "https://github.com/dtolnay/thiserror.git" thiserror
check_repo "https://github.com/dtolnay/syn.git" syn
check_repo "https://github.com/serde-rs/serde.git" serde
check_repo "https://github.com/rust-lang/rustlings.git" rustlings
check_repo "https://github.com/rust-lang/rustup.git" rustup
check_repo "https://github.com/SergioBenitez/Rocket.git" Rocket
check_repo "https://github.com/rustls/rustls.git" rustls
check_repo "https://github.com/rust-lang/rust-bindgen.git" rust-bindgen
check_repo "https://github.com/hyperium/hyper.git" hyper
check_repo "https://github.com/actix/actix.git" actix
check_repo "https://github.com/denoland/deno.git" denoland_deno
# cleanup temp dir
echo removing tmp_dir $tmp_dir
rm -rf $tmp_dir
# figure out the exit code
for status in ${STATUSES[@]}
do
if [ $status -eq 1 ]; then
echo "formatting diff found 💔"
return 1
fi
done
echo "no diff found 😊"
}
main

View file

@ -91,14 +91,28 @@ case ${INTEGRATION} in
cd -
;;
crater)
git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git
git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_lib_tests
cd -
;;
bitflags)
git clone --depth=1 https://github.com/bitflags/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_all_tests
cd -
;;
error-chain | tempdir)
git clone --depth=1 https://github.com/rust-lang-deprecated/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_all_tests
cd -
;;
*)
git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git
git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_all_tests

View file

@ -22,7 +22,7 @@ dependencies = [
[[package]]
name = "rustfmt-config_proc_macro"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",

View file

@ -1,6 +1,6 @@
[package]
name = "rustfmt-config_proc_macro"
version = "0.2.0"
version = "0.3.0"
edition = "2018"
description = "A collection of procedural macros for rustfmt"
license = "Apache-2.0/MIT"

View file

@ -1,8 +1,10 @@
//! This module provides utilities for handling attributes on variants
//! of `config_type` enum. Currently there are two types of attributes
//! that could appear on the variants of `config_type` enum: `doc_hint`
//! and `value`. Both comes in the form of name-value pair whose value
//! is string literal.
//! of `config_type` enum. Currently there are the following attributes
//! that could appear on the variants of `config_type` enum:
//!
//! - `doc_hint`: name-value pair whose value is string literal
//! - `value`: name-value pair whose value is string literal
//! - `unstable_variant`: name only
/// Returns the value of the first `doc_hint` attribute in the given slice or
/// `None` if `doc_hint` attribute is not available.
@ -27,6 +29,11 @@ pub fn find_config_value(attrs: &[syn::Attribute]) -> Option<String> {
attrs.iter().filter_map(config_value).next()
}
/// Returns `true` if the there is at least one `unstable` attribute in the given slice.
pub fn any_unstable_variant(attrs: &[syn::Attribute]) -> bool {
attrs.iter().any(is_unstable_variant)
}
/// Returns a string literal value if the given attribute is `value`
/// attribute or `None` otherwise.
pub fn config_value(attr: &syn::Attribute) -> Option<String> {
@ -38,6 +45,11 @@ pub fn is_config_value(attr: &syn::Attribute) -> bool {
is_attr_name_value(attr, "value")
}
/// Returns `true` if the given attribute is an `unstable` attribute.
pub fn is_unstable_variant(attr: &syn::Attribute) -> bool {
is_attr_path(attr, "unstable_variant")
}
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
attr.parse_meta().ok().map_or(false, |meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true,
@ -45,6 +57,13 @@ fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
})
}
fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool {
attr.parse_meta().ok().map_or(false, |meta| match meta {
syn::Meta::Path(path) if path.is_ident(name) => true,
_ => false,
})
}
fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
attr.parse_meta().ok().and_then(|meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue {

View file

@ -1,5 +1,6 @@
use proc_macro2::TokenStream;
use quote::quote;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use crate::attrs::*;
use crate::utils::*;
@ -47,12 +48,23 @@ fn process_variant(variant: &syn::Variant) -> TokenStream {
let metas = variant
.attrs
.iter()
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr));
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr));
let attrs = fold_quote(metas, |meta| quote!(#meta));
let syn::Variant { ident, fields, .. } = variant;
quote!(#attrs #ident #fields)
}
/// Return the correct syntax to pattern match on the enum variant, discarding all
/// internal field data.
fn fields_in_variant(variant: &syn::Variant) -> TokenStream {
// With thanks to https://stackoverflow.com/a/65182902
match &variant.fields {
syn::Fields::Unnamed(_) => quote_spanned! { variant.span() => (..) },
syn::Fields::Unit => quote_spanned! { variant.span() => },
syn::Fields::Named(_) => quote_spanned! { variant.span() => {..} },
}
}
fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let doc_hint = variants
.iter()
@ -60,12 +72,26 @@ fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
.collect::<Vec<_>>()
.join("|");
let doc_hint = format!("[{}]", doc_hint);
let variant_stables = variants
.iter()
.map(|v| (&v.ident, fields_in_variant(&v), !unstable_of_variant(v)));
let match_patterns = fold_quote(variant_stables, |(v, fields, stable)| {
quote! {
#ident::#v #fields => #stable,
}
});
quote! {
use crate::config::ConfigType;
impl ConfigType for #ident {
fn doc_hint() -> String {
#doc_hint.to_owned()
}
fn stable_variant(&self) -> bool {
match self {
#match_patterns
}
}
}
}
}
@ -123,13 +149,21 @@ fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream {
}
fn doc_hint_of_variant(variant: &syn::Variant) -> String {
find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string())
let mut text = find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string());
if unstable_of_variant(&variant) {
text.push_str(" (unstable)")
};
text
}
fn config_value_of_variant(variant: &syn::Variant) -> String {
find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string())
}
fn unstable_of_variant(variant: &syn::Variant) -> bool {
any_unstable_variant(&variant.attrs)
}
fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let arms = fold_quote(variants.iter(), |v| {
let v_ident = &v.ident;

View file

@ -69,3 +69,16 @@ pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream {
TokenStream::from_str("").unwrap()
}
}
/// Used to conditionally output the TokenStream for tests that should be run as part of rustfmts
/// test suite, but should be ignored when running in the rust-lang/rust test suite.
#[proc_macro_attribute]
pub fn rustfmt_only_ci_test(_args: TokenStream, input: TokenStream) -> TokenStream {
if option_env!("RUSTFMT_CI").is_some() {
input
} else {
let mut token_stream = TokenStream::from_str("#[ignore]").unwrap();
token_stream.extend(input);
token_stream
}
}

View file

@ -1,6 +1,7 @@
pub mod config {
pub trait ConfigType: Sized {
fn doc_hint() -> String;
fn stable_variant(&self) -> bool;
}
}

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2022-06-21"
components = ["rustc-dev"]
channel = "nightly-2023-01-24"
components = ["llvm-tools", "rustc-dev"]

View file

@ -336,7 +336,7 @@ impl Rewrite for ast::Attribute {
} else {
let should_skip = self
.ident()
.map(|s| context.skip_context.skip_attribute(s.name.as_str()))
.map(|s| context.skip_context.attributes.skip(s.name.as_str()))
.unwrap_or(false);
let prefix = attr_prefix(self);
@ -390,7 +390,7 @@ impl Rewrite for [ast::Attribute] {
// Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]`
// or `#![rustfmt::skip::attributes(derive)]`
let skip_derives = context.skip_context.skip_attribute("derive");
let skip_derives = context.skip_context.attributes.skip("derive");
// This is not just a simple map because we need to handle doc comments
// (where we take as many doc comment attributes as possible) and possibly

View file

@ -136,7 +136,7 @@ fn make_opts() -> Options {
"l",
"files-with-diff",
"Prints the names of mismatched files that were formatted. Prints the names of \
files that would be formated when used with `--check` mode. ",
files that would be formatted when used with `--check` mode. ",
);
opts.optmulti(
"",

View file

@ -198,12 +198,10 @@ fn convert_message_format_to_rustfmt_args(
Ok(())
}
"human" => Ok(()),
_ => {
return Err(format!(
"invalid --message-format value: {}. Allowed values are: short|json|human",
message_format
));
}
_ => Err(format!(
"invalid --message-format value: {}. Allowed values are: short|json|human",
message_format
)),
}
}
@ -215,7 +213,7 @@ fn print_usage_to_stderr(reason: &str) {
.expect("failed to write to stderr");
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verbosity {
Verbose,
Normal,

View file

@ -70,9 +70,9 @@ fn mandatory_separator() {
.is_err()
);
assert!(
!Opts::command()
Opts::command()
.try_get_matches_from(&["test", "--", "--emit"])
.is_err()
.is_ok()
);
}

View file

@ -70,10 +70,64 @@ use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::utils::{
self, first_line_width, last_line_extendable, last_line_width, mk_sp, rewrite_ident,
trimmed_last_line_width, wrap_str,
self, filtered_str_fits, first_line_width, last_line_extendable, last_line_width, mk_sp,
rewrite_ident, trimmed_last_line_width, wrap_str,
};
/// Provides the original input contents from the span
/// of a chain element with trailing spaces trimmed.
fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> {
context.snippet_provider.span_to_snippet(span).map(|s| {
s.lines()
.map(|l| l.trim_end())
.collect::<Vec<_>>()
.join("\n")
})
}
fn format_chain_item(
item: &ChainItem,
context: &RewriteContext<'_>,
rewrite_shape: Shape,
allow_overflow: bool,
) -> Option<String> {
if allow_overflow {
item.rewrite(context, rewrite_shape)
.or_else(|| format_overflow_style(item.span, context))
} else {
item.rewrite(context, rewrite_shape)
}
}
fn get_block_child_shape(
prev_ends_with_block: bool,
context: &RewriteContext<'_>,
shape: Shape,
) -> Shape {
if prev_ends_with_block {
shape.block_indent(0)
} else {
shape.block_indent(context.config.tab_spaces())
}
.with_max_width(context.config)
}
fn get_visual_style_child_shape(
context: &RewriteContext<'_>,
shape: Shape,
offset: usize,
parent_overflowing: bool,
) -> Option<Shape> {
if !parent_overflowing {
shape
.with_max_width(context.config)
.offset_left(offset)
.map(|s| s.visual_indent(0))
} else {
Some(shape.visual_indent(offset))
}
}
pub(crate) fn rewrite_chain(
expr: &ast::Expr,
context: &RewriteContext<'_>,
@ -496,6 +550,8 @@ struct ChainFormatterShared<'a> {
// The number of children in the chain. This is not equal to `self.children.len()`
// because `self.children` will change size as we process the chain.
child_count: usize,
// Whether elements are allowed to overflow past the max_width limit
allow_overflow: bool,
}
impl<'a> ChainFormatterShared<'a> {
@ -505,6 +561,8 @@ impl<'a> ChainFormatterShared<'a> {
rewrites: Vec::with_capacity(chain.children.len() + 1),
fits_single_line: false,
child_count: chain.children.len(),
// TODO(calebcartwright)
allow_overflow: false,
}
}
@ -517,6 +575,14 @@ impl<'a> ChainFormatterShared<'a> {
}
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
for item in &self.children[..self.children.len() - 1] {
let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
self.rewrites.push(rewrite);
}
Some(())
}
// Rewrite the last child. The last child of a chain requires special treatment. We need to
// know whether 'overflowing' the last child make a better formatting:
//
@ -729,22 +795,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
Some(
if self.root_ends_with_block {
shape.block_indent(0)
} else {
shape.block_indent(context.config.tab_spaces())
}
.with_max_width(context.config),
)
let block_end = self.root_ends_with_block;
Some(get_block_child_shape(block_end, context, shape))
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
for item in &self.shared.children[..self.shared.children.len() - 1] {
let rewrite = item.rewrite(context, child_shape)?;
self.shared.rewrites.push(rewrite);
}
Some(())
self.shared.format_children(context, child_shape)
}
fn format_last_child(
@ -808,15 +864,14 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
.visual_indent(self.offset)
.sub_width(self.offset)?;
let rewrite = item.rewrite(context, child_shape)?;
match wrap_str(rewrite, context.config.max_width(), shape) {
Some(rewrite) => root_rewrite.push_str(&rewrite),
None => {
// We couldn't fit in at the visual indent, try the last
// indent.
let rewrite = item.rewrite(context, parent_shape)?;
root_rewrite.push_str(&rewrite);
self.offset = 0;
}
if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
root_rewrite.push_str(&rewrite);
} else {
// We couldn't fit in at the visual indent, try the last
// indent.
let rewrite = item.rewrite(context, parent_shape)?;
root_rewrite.push_str(&rewrite);
self.offset = 0;
}
self.shared.children = &self.shared.children[1..];
@ -827,18 +882,17 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
shape
.with_max_width(context.config)
.offset_left(self.offset)
.map(|s| s.visual_indent(0))
get_visual_style_child_shape(
context,
shape,
self.offset,
// TODO(calebcartwright): self.shared.permissibly_overflowing_parent,
false,
)
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
for item in &self.shared.children[..self.shared.children.len() - 1] {
let rewrite = item.rewrite(context, child_shape)?;
self.shared.rewrites.push(rewrite);
}
Some(())
self.shared.format_children(context, child_shape)
}
fn format_last_child(

View file

@ -1,4 +1,5 @@
use crate::config::file_lines::FileLines;
use crate::config::macro_names::MacroSelectors;
use crate::config::options::{IgnoreList, WidthHeuristics};
/// Trait for types that can be used in `Config`.
@ -6,6 +7,14 @@ pub(crate) 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;
/// Return `true` if the variant (i.e. value of this type) is stable.
///
/// By default, return true for all values. Enums annotated with `#[config_type]`
/// are automatically implemented, based on the `#[unstable_variant]` annotation.
fn stable_variant(&self) -> bool {
true
}
}
impl ConfigType for bool {
@ -38,6 +47,12 @@ impl ConfigType for FileLines {
}
}
impl ConfigType for MacroSelectors {
fn doc_hint() -> String {
String::from("[<string>, ...]")
}
}
impl ConfigType for WidthHeuristics {
fn doc_hint() -> String {
String::new()
@ -51,6 +66,13 @@ impl ConfigType for IgnoreList {
}
macro_rules! create_config {
// Options passed in to the macro.
//
// - $i: the ident name of the option
// - $ty: the type of the option value
// - $def: the default value of the option
// - $stb: true if the option is stable
// - $dstring: description of the option
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[cfg(test)]
use std::collections::HashSet;
@ -61,9 +83,12 @@ macro_rules! create_config {
#[derive(Clone)]
#[allow(unreachable_pub)]
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,
// For each config item, we store:
//
// - 0: true if the value has been access
// - 1: true if the option was manually initialized
// - 2: the option value
// - 3: true if the option is unstable
$($i: (Cell<bool>, bool, $ty, bool)),+
}
@ -102,6 +127,7 @@ macro_rules! create_config {
| "array_width"
| "chain_width" => self.0.set_heuristics(),
"merge_imports" => self.0.set_merge_imports(),
"fn_args_layout" => self.0.set_fn_args_layout(),
&_ => (),
}
}
@ -143,24 +169,20 @@ macro_rules! create_config {
fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$(
if let Some(val) = parsed.$i {
if self.$i.3 {
if let Some(option_value) = parsed.$i {
let option_stable = self.$i.3;
if $crate::config::config_type::is_stable_option_and_value(
stringify!($i), option_stable, &option_value
) {
self.$i.1 = true;
self.$i.2 = val;
} else {
if crate::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.$i.2 = option_value;
}
}
)+
self.set_heuristics();
self.set_ignore(dir);
self.set_merge_imports();
self.set_fn_args_layout();
self
}
@ -221,12 +243,22 @@ macro_rules! create_config {
match key {
$(
stringify!($i) => {
self.$i.1 = true;
self.$i.2 = val.parse::<$ty>()
let option_value = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!($ty)));
// Users are currently allowed to set unstable
// options/variants via the `--config` options override.
//
// There is ongoing discussion about how to move forward here:
// https://github.com/rust-lang/rustfmt/pull/5379
//
// For now, do not validate whether the option or value is stable,
// just always set it.
self.$i.1 = true;
self.$i.2 = option_value;
}
)+
_ => panic!("Unknown config key in override: {}", key)
@ -243,14 +275,21 @@ macro_rules! create_config {
| "array_width"
| "chain_width" => self.set_heuristics(),
"merge_imports" => self.set_merge_imports(),
"fn_args_layout" => self.set_fn_args_layout(),
&_ => (),
}
}
#[allow(unreachable_pub)]
pub fn is_hidden_option(name: &str) -> bool {
const HIDE_OPTIONS: [&str; 5] =
["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"];
const HIDE_OPTIONS: [&str; 6] = [
"verbose",
"verbose_diff",
"file_lines",
"width_heuristics",
"merge_imports",
"fn_args_layout"
];
HIDE_OPTIONS.contains(&name)
}
@ -400,6 +439,18 @@ macro_rules! create_config {
}
}
fn set_fn_args_layout(&mut self) {
if self.was_set().fn_args_layout() {
eprintln!(
"Warning: the `fn_args_layout` option is deprecated. \
Use `fn_params_layout`. instead"
);
if !self.was_set().fn_params_layout() {
self.fn_params_layout.2 = self.fn_args_layout();
}
}
}
#[allow(unreachable_pub)]
/// Returns `true` if the config key was explicitly set and is the default value.
pub fn is_default(&self, key: &str) -> bool {
@ -424,3 +475,38 @@ macro_rules! create_config {
}
)
}
pub(crate) fn is_stable_option_and_value<T>(
option_name: &str,
option_stable: bool,
option_value: &T,
) -> bool
where
T: PartialEq + std::fmt::Debug + ConfigType,
{
let nightly = crate::is_nightly_channel!();
let variant_stable = option_value.stable_variant();
match (nightly, option_stable, variant_stable) {
// Stable with an unstable option
(false, false, _) => {
eprintln!(
"Warning: can't set `{} = {:?}`, unstable features are only \
available in nightly channel.",
option_name, option_value
);
false
}
// Stable with a stable option, but an unstable variant
(false, true, false) => {
eprintln!(
"Warning: can't set `{} = {:?}`, unstable variants are only \
available in nightly channel.",
option_name, option_value
);
false
}
// Nightly: everything allowed
// Stable with stable option and variant: allowed
(true, _, _) | (false, true, true) => true,
}
}

View file

@ -0,0 +1,118 @@
//! This module contains types and functions to support formatting specific macros.
use itertools::Itertools;
use std::{fmt, str};
use serde::{Deserialize, Serialize};
use serde_json as json;
use thiserror::Error;
/// Defines the name of a macro.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub struct MacroName(String);
impl MacroName {
pub fn new(other: String) -> Self {
Self(other)
}
}
impl fmt::Display for MacroName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<MacroName> for String {
fn from(other: MacroName) -> Self {
other.0
}
}
/// Defines a selector to match against a macro.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum MacroSelector {
Name(MacroName),
All,
}
impl fmt::Display for MacroSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Name(name) => name.fmt(f),
Self::All => write!(f, "*"),
}
}
}
impl str::FromStr for MacroSelector {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"*" => MacroSelector::All,
name => MacroSelector::Name(MacroName(name.to_owned())),
})
}
}
/// A set of macro selectors.
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct MacroSelectors(pub Vec<MacroSelector>);
impl fmt::Display for MacroSelectors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.iter().format(", "))
}
}
#[derive(Error, Debug)]
pub enum MacroSelectorsError {
#[error("{0}")]
Json(json::Error),
}
// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for MacroSelectors {
type Err = MacroSelectorsError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let raw: Vec<&str> = json::from_str(s).map_err(MacroSelectorsError::Json)?;
Ok(Self(
raw.into_iter()
.map(|raw| {
MacroSelector::from_str(raw).expect("MacroSelector from_str is infallible")
})
.collect(),
))
}
}
#[cfg(test)]
mod test {
use super::*;
use std::str::FromStr;
#[test]
fn macro_names_from_str() {
let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap();
assert_eq!(
macro_names,
MacroSelectors(
[
MacroSelector::Name(MacroName("foo".to_owned())),
MacroSelector::All,
MacroSelector::Name(MacroName("bar".to_owned()))
]
.into_iter()
.collect()
)
);
}
#[test]
fn macro_names_display() {
let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap();
assert_eq!(format!("{}", macro_names), "foo, *, bar");
}
}

View file

@ -13,15 +13,20 @@ pub use crate::config::file_lines::{FileLines, FileName, Range};
#[allow(unreachable_pub)]
pub use crate::config::lists::*;
#[allow(unreachable_pub)]
pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
#[allow(unreachable_pub)]
pub use crate::config::options::*;
#[macro_use]
pub(crate) mod config_type;
#[macro_use]
#[allow(unreachable_pub)]
pub(crate) mod options;
pub(crate) mod file_lines;
#[allow(unreachable_pub)]
pub(crate) mod lists;
pub(crate) mod macro_names;
// This macro defines configuration options used in rustfmt. Each option
// is defined as follows:
@ -67,6 +72,8 @@ create_config! {
format_macro_matchers: bool, false, false,
"Format the metavariable matching patterns in macros";
format_macro_bodies: bool, true, false, "Format the bodies of macros";
skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false,
"Skip formatting the bodies of macros invoked with the following names.";
hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
"Format hexadecimal integer literals";
@ -119,7 +126,9 @@ create_config! {
force_multiline_blocks: bool, false, false,
"Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_layout: Density, Density::Tall, true,
"Control the layout of arguments in a function";
"(deprecated: use fn_params_layout instead)";
fn_params_layout: Density, Density::Tall, true,
"Control the layout of parameters in function signatures.";
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
"Brace style for control flow constructs";
@ -175,7 +184,7 @@ create_config! {
make_backup: bool, false, false, "Backup changed files";
print_misformatted_file_names: bool, false, true,
"Prints the names of mismatched files that were formatted. Prints the names of \
files that would be formated when used with `--check` mode. ";
files that would be formatted when used with `--check` mode. ";
}
#[derive(Error, Debug)]
@ -191,6 +200,7 @@ impl PartialConfig {
cloned.width_heuristics = None;
cloned.print_misformatted_file_names = None;
cloned.merge_imports = None;
cloned.fn_args_layout = None;
::toml::to_string(&cloned).map_err(ToTomlError)
}
@ -403,11 +413,21 @@ mod test {
use super::*;
use std::str;
use crate::config::macro_names::MacroName;
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
#[allow(dead_code)]
mod mock {
use super::super::*;
use rustfmt_config_proc_macro::config_type;
#[config_type]
pub(crate) enum PartiallyUnstableOption {
V1,
V2,
#[unstable_variant]
V3,
}
create_config! {
// Options that are used by the generated functions
@ -427,6 +447,12 @@ mod test {
"Merge imports";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
// fn_args_layout renamed to fn_params_layout
fn_args_layout: Density, Density::Tall, true,
"(deprecated: use fn_params_layout instead)";
fn_params_layout: Density, Density::Tall, true,
"Control the layout of parameters in a function signatures.";
// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true,
"Whether to use different formatting for items and \
@ -451,6 +477,63 @@ mod test {
// Options that are used by the tests
stable_option: bool, false, true, "A stable option";
unstable_option: bool, false, false, "An unstable option";
partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true,
"A partially unstable option";
}
#[cfg(test)]
mod partially_unstable_option {
use super::{Config, PartialConfig, PartiallyUnstableOption};
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
use std::path::Path;
/// From the config file, we can fill with a stable variant
#[test]
fn test_from_toml_stable_value() {
let toml = r#"
partially_unstable_option = "V2"
"#;
let partial_config: PartialConfig = toml::from_str(toml).unwrap();
let config = Config::default();
let config = config.fill_from_parsed_config(partial_config, Path::new(""));
assert_eq!(
config.partially_unstable_option(),
PartiallyUnstableOption::V2
);
}
/// From the config file, we cannot fill with an unstable variant (stable only)
#[stable_only_test]
#[test]
fn test_from_toml_unstable_value_on_stable() {
let toml = r#"
partially_unstable_option = "V3"
"#;
let partial_config: PartialConfig = toml::from_str(toml).unwrap();
let config = Config::default();
let config = config.fill_from_parsed_config(partial_config, Path::new(""));
assert_eq!(
config.partially_unstable_option(),
// default value from config, i.e. fill failed
PartiallyUnstableOption::V1
);
}
/// From the config file, we can fill with an unstable variant (nightly only)
#[nightly_only_test]
#[test]
fn test_from_toml_unstable_value_on_nightly() {
let toml = r#"
partially_unstable_option = "V3"
"#;
let partial_config: PartialConfig = toml::from_str(toml).unwrap();
let config = Config::default();
let config = config.fill_from_parsed_config(partial_config, Path::new(""));
assert_eq!(
config.partially_unstable_option(),
PartiallyUnstableOption::V3
);
}
}
}
@ -489,6 +572,11 @@ mod test {
assert_eq!(config.was_set().verbose(), false);
}
const PRINT_DOCS_STABLE_OPTION: &str = "stable_option <boolean> Default: false";
const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option <boolean> Default: false (unstable)";
const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str =
"partially_unstable_option [V1|V2|V3 (unstable)] Default: V1";
#[test]
fn test_print_docs_exclude_unstable() {
use self::mock::Config;
@ -497,10 +585,9 @@ mod test {
Config::print_docs(&mut output, false);
let s = str::from_utf8(&output).unwrap();
assert_eq!(s.contains("stable_option"), true);
assert_eq!(s.contains("unstable_option"), false);
assert_eq!(s.contains("(unstable)"), false);
assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false);
assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
@ -511,9 +598,9 @@ mod test {
Config::print_docs(&mut output, true);
let s = str::from_utf8(&output).unwrap();
assert_eq!(s.contains("stable_option"), true);
assert_eq!(s.contains("unstable_option"), true);
assert_eq!(s.contains("(unstable)"), true);
assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true);
assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
@ -541,6 +628,7 @@ normalize_doc_attributes = false
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
skip_macro_invocations = []
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
@ -567,7 +655,7 @@ enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_args_layout = "Tall"
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
@ -921,4 +1009,45 @@ make_backup = false
assert_eq!(config.single_line_if_else_max_width(), 100);
}
}
#[cfg(test)]
mod partially_unstable_option {
use super::mock::{Config, PartiallyUnstableOption};
use super::*;
/// From the command line, we can override with a stable variant.
#[test]
fn test_override_stable_value() {
let mut config = Config::default();
config.override_value("partially_unstable_option", "V2");
assert_eq!(
config.partially_unstable_option(),
PartiallyUnstableOption::V2
);
}
/// From the command line, we can override with an unstable variant.
#[test]
fn test_override_unstable_value() {
let mut config = Config::default();
config.override_value("partially_unstable_option", "V3");
assert_eq!(
config.partially_unstable_option(),
PartiallyUnstableOption::V3
);
}
}
#[test]
fn test_override_skip_macro_invocations() {
let mut config = Config::default();
config.override_value("skip_macro_invocations", r#"["*", "println"]"#);
assert_eq!(
config.skip_macro_invocations(),
MacroSelectors(vec![
MacroSelector::All,
MacroSelector::Name(MacroName::new("println".to_owned()))
])
);
}
}

View file

@ -29,9 +29,9 @@ use crate::spanned::Spanned;
use crate::string::{rewrite_string, StringFormat};
use crate::types::{rewrite_path, PathContext};
use crate::utils::{
colon_spaces, contains_skip, count_newlines, first_line_ends_with, inner_attributes,
last_line_extendable, last_line_width, mk_sp, outer_attributes, semicolon_for_expr,
unicode_str_width, wrap_str,
colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with,
inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes,
semicolon_for_expr, unicode_str_width, wrap_str,
};
use crate::vertical::rewrite_with_alignment;
use crate::visitor::FmtVisitor;
@ -2050,8 +2050,7 @@ fn choose_rhs<R: Rewrite>(
match (orig_rhs, new_rhs) {
(Some(ref orig_rhs), Some(ref new_rhs))
if wrap_str(new_rhs.clone(), context.config.max_width(), new_shape)
.is_none() =>
if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
{
Some(format!("{}{}", before_space_str, orig_rhs))
}

View file

@ -251,8 +251,8 @@ fn flatten_use_trees(
use_trees: Vec<UseTree>,
import_granularity: ImportGranularity,
) -> Vec<UseTree> {
// Return non-sorted single occurance of the use-trees text string;
// order is by first occurance of the use-tree.
// Return non-sorted single occurrence of the use-trees text string;
// order is by first occurrence of the use-tree.
use_trees
.into_iter()
.flat_map(|tree| tree.flatten(import_granularity))

View file

@ -1084,7 +1084,11 @@ pub(crate) fn format_trait(
let item_snippet = context.snippet(item.span);
if let Some(lo) = item_snippet.find('/') {
// 1 = `{`
let comment_hi = body_lo - BytePos(1);
let comment_hi = if generics.params.len() > 0 {
generics.span.lo() - BytePos(1)
} else {
body_lo - BytePos(1)
};
let comment_lo = item.span.lo() + BytePos(lo as u32);
if comment_lo < comment_hi {
match recover_missing_comment_in_span(
@ -1241,7 +1245,7 @@ fn format_unit_struct(
) -> Option<String> {
let header_str = format_header(context, p.prefix, p.ident, p.vis, offset);
let generics_str = if let Some(generics) = p.generics {
let hi = context.snippet_provider.span_before(p.span, ";");
let hi = context.snippet_provider.span_before_last(p.span, ";");
format_generics(
context,
generics,
@ -2602,7 +2606,7 @@ fn rewrite_params(
&param_items,
context
.config
.fn_args_layout()
.fn_params_layout()
.to_list_tactic(param_items.len()),
Separator::Comma,
one_line_budget,

View file

@ -297,9 +297,9 @@ where
} else {
inner_item.as_ref()
};
let mut item_last_line_width = item_last_line.len() + item_sep_len;
let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len;
if item_last_line.starts_with(&**indent_str) {
item_last_line_width -= indent_str.len();
item_last_line_width -= unicode_str_width(indent_str);
}
if !item.is_substantial() {
@ -449,7 +449,7 @@ where
} else if starts_with_newline(comment) {
false
} else {
comment.trim().contains('\n') || comment.trim().len() > width
comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width
};
rewrite_comment(
@ -465,7 +465,7 @@ where
if !starts_with_newline(comment) {
if formatting.align_comments {
let mut comment_alignment =
post_comment_alignment(item_max_width, inner_item.len());
post_comment_alignment(item_max_width, unicode_str_width(inner_item));
if first_line_width(&formatted_comment)
+ last_line_width(&result)
+ comment_alignment
@ -475,7 +475,7 @@ where
item_max_width = None;
formatted_comment = rewrite_post_comment(&mut item_max_width)?;
comment_alignment =
post_comment_alignment(item_max_width, inner_item.len());
post_comment_alignment(item_max_width, unicode_str_width(inner_item));
}
for _ in 0..=comment_alignment {
result.push(' ');
@ -533,7 +533,7 @@ where
let mut first = true;
for item in items.clone().into_iter().skip(i) {
let item = item.as_ref();
let inner_item_width = item.inner_as_ref().len();
let inner_item_width = unicode_str_width(item.inner_as_ref());
if !first
&& (item.is_different_group()
|| item.post_comment.is_none()
@ -552,8 +552,8 @@ where
max_width
}
fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize) -> usize {
item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
fn post_comment_alignment(item_max_width: Option<usize>, inner_item_width: usize) -> usize {
item_max_width.unwrap_or(0).saturating_sub(inner_item_width)
}
pub(crate) struct ListItems<'a, I, F1, F2, F3>

View file

@ -35,8 +35,8 @@ use crate::shape::{Indent, Shape};
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::utils::{
format_visibility, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces,
rewrite_ident, trim_left_preserve_layout, wrap_str, NodeIdExt,
filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt,
};
use crate::visitor::FmtVisitor;
@ -157,7 +157,8 @@ pub(crate) fn rewrite_macro(
) -> Option<String> {
let should_skip = context
.skip_context
.skip_macro(context.snippet(mac.path.span));
.macros
.skip(context.snippet(mac.path.span));
if should_skip {
None
} else {
@ -1265,15 +1266,14 @@ impl MacroBranch {
}
}
};
let new_body = wrap_str(
new_body_snippet.snippet.to_string(),
config.max_width(),
shape,
)?;
if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
return None;
}
// Indent the body since it is in a block.
let indent_str = body_indent.to_string(&config);
let mut new_body = LineClasses::new(new_body.trim_end())
let mut new_body = LineClasses::new(new_body_snippet.snippet.trim_end())
.enumerate()
.fold(
(String::new(), true),

View file

@ -2,33 +2,84 @@
use rustc_ast::ast;
use rustc_ast_pretty::pprust;
use std::collections::HashSet;
/// Take care of skip name stack. You can update it by attributes slice or
/// by other context. Query this context to know if you need skip a block.
/// Track which blocks of code are to be skipped when formatting.
///
/// You can update it by:
///
/// - attributes slice
/// - manually feeding values into the underlying contexts
///
/// Query this context to know if you need to skip a block.
#[derive(Default, Clone)]
pub(crate) struct SkipContext {
macros: Vec<String>,
attributes: Vec<String>,
pub(crate) macros: SkipNameContext,
pub(crate) attributes: SkipNameContext,
}
impl SkipContext {
pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) {
self.macros.append(&mut get_skip_names("macros", attrs));
self.attributes
.append(&mut get_skip_names("attributes", attrs));
self.macros.extend(get_skip_names("macros", attrs));
self.attributes.extend(get_skip_names("attributes", attrs));
}
pub(crate) fn update(&mut self, mut other: SkipContext) {
self.macros.append(&mut other.macros);
self.attributes.append(&mut other.attributes);
pub(crate) fn update(&mut self, other: SkipContext) {
let SkipContext { macros, attributes } = other;
self.macros.update(macros);
self.attributes.update(attributes);
}
}
/// Track which names to skip.
///
/// Query this context with a string to know whether to skip it.
#[derive(Clone)]
pub(crate) enum SkipNameContext {
All,
Values(HashSet<String>),
}
impl Default for SkipNameContext {
fn default() -> Self {
Self::Values(Default::default())
}
}
impl Extend<String> for SkipNameContext {
fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
match self {
Self::All => {}
Self::Values(values) => values.extend(iter),
}
}
}
impl SkipNameContext {
pub(crate) fn update(&mut self, other: Self) {
match (self, other) {
// If we're already skipping everything, nothing more can be added
(Self::All, _) => {}
// If we want to skip all, set it
(this, Self::All) => {
*this = Self::All;
}
// If we have some new values to skip, add them
(Self::Values(existing_values), Self::Values(new_values)) => {
existing_values.extend(new_values)
}
}
}
pub(crate) fn skip_macro(&self, name: &str) -> bool {
self.macros.iter().any(|n| n == name)
pub(crate) fn skip(&self, name: &str) -> bool {
match self {
Self::All => true,
Self::Values(values) => values.contains(name),
}
}
pub(crate) fn skip_attribute(&self, name: &str) -> bool {
self.attributes.iter().any(|n| n == name)
pub(crate) fn skip_all(&mut self) {
*self = Self::All;
}
}

View file

@ -27,8 +27,13 @@ impl ConfigurationSection {
lazy_static! {
static ref CONFIG_NAME_REGEX: regex::Regex =
regex::Regex::new(r"^## `([^`]+)`").expect("failed creating configuration pattern");
// Configuration values, which will be passed to `from_str`:
//
// - must be prefixed with `####`
// - must be wrapped in backticks
// - may by wrapped in double quotes (which will be stripped)
static ref CONFIG_VALUE_REGEX: regex::Regex =
regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#)
regex::Regex::new(r#"^#### `"?([^`]+?)"?`"#)
.expect("failed creating configuration value pattern");
}

View file

@ -982,11 +982,7 @@ fn rustfmt() -> PathBuf {
assert!(
me.is_file() || me.with_extension("exe").is_file(),
"{}",
if cfg!(release) {
"no rustfmt bin, try running `cargo build --release` before testing"
} else {
"no rustfmt bin, try running `cargo build` before testing"
}
"no rustfmt bin, try running `cargo build` or `cargo build --release` before testing"
);
me
}

View file

@ -941,6 +941,28 @@ fn join_bounds_inner(
ast::GenericBound::Trait(..) => last_line_extendable(s),
};
// Whether a GenericBound item is a PathSegment segment that includes internal array
// that contains more than one item
let is_item_with_multi_items_array = |item: &ast::GenericBound| match item {
ast::GenericBound::Trait(ref poly_trait_ref, ..) => {
let segments = &poly_trait_ref.trait_ref.path.segments;
if segments.len() > 1 {
true
} else {
if let Some(args_in) = &segments[0].args {
matches!(
args_in.deref(),
ast::GenericArgs::AngleBracketed(bracket_args)
if bracket_args.args.len() > 1
)
} else {
false
}
}
}
_ => false,
};
let result = items.iter().enumerate().try_fold(
(String::new(), None, false),
|(strs, prev_trailing_span, prev_extendable), (i, item)| {
@ -1035,10 +1057,24 @@ fn join_bounds_inner(
},
)?;
if !force_newline
&& items.len() > 1
&& (result.0.contains('\n') || result.0.len() > shape.width)
{
// Whether to retry with a forced newline:
// Only if result is not already multiline and did not exceed line width,
// and either there is more than one item;
// or the single item is of type `Trait`,
// and any of the internal arrays contains more than one item;
let retry_with_force_newline = match context.config.version() {
Version::One => {
!force_newline
&& items.len() > 1
&& (result.0.contains('\n') || result.0.len() > shape.width)
}
Version::Two if force_newline => false,
Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
Version::Two if items.len() > 1 => true,
Version::Two => is_item_with_multi_items_array(&items[0]),
};
if retry_with_force_newline {
join_bounds_inner(context, shape, items, need_indent, true)
} else {
Some(result.0)

View file

@ -384,14 +384,15 @@ macro_rules! skip_out_of_file_lines_range_visitor {
// Wraps String in an Option. Returns Some when the string adheres to the
// Rewrite constraints defined for the Rewrite trait and None otherwise.
pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
if is_valid_str(&filter_normal_code(&s), max_width, shape) {
if filtered_str_fits(&s, max_width, shape) {
Some(s)
} else {
None
}
}
fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool {
let snippet = &filter_normal_code(snippet);
if !snippet.is_empty() {
// First line must fits with `shape.width`.
if first_line_width(snippet) > shape.width {

View file

@ -8,7 +8,7 @@ use rustc_span::{symbol, BytePos, Pos, Span};
use crate::attr::*;
use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
use crate::config::Version;
use crate::config::{BraceStyle, Config};
use crate::config::{BraceStyle, Config, MacroSelector};
use crate::coverage::transform_missing_snippet;
use crate::items::{
format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate,
@ -770,6 +770,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
snippet_provider: &'a SnippetProvider,
report: FormatReport,
) -> FmtVisitor<'a> {
let mut skip_context = SkipContext::default();
let mut macro_names = Vec::new();
for macro_selector in config.skip_macro_invocations().0 {
match macro_selector {
MacroSelector::Name(name) => macro_names.push(name.to_string()),
MacroSelector::All => skip_context.macros.skip_all(),
}
}
skip_context.macros.extend(macro_names);
FmtVisitor {
parent_context: None,
parse_sess: parse_session,
@ -784,7 +793,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
is_macro_def: false,
macro_rewrite_failure: false,
report,
skip_context: Default::default(),
skip_context,
}
}

View file

@ -4,6 +4,8 @@ use std::env;
use std::path::Path;
use std::process::Command;
use rustfmt_config_proc_macro::rustfmt_only_ci_test;
/// Run the cargo-fmt executable and return its output.
fn cargo_fmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
@ -47,7 +49,7 @@ macro_rules! assert_that {
};
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn version() {
assert_that!(&["--version"], starts_with("rustfmt "));
@ -56,7 +58,7 @@ fn version() {
assert_that!(&["--", "--version"], starts_with("rustfmt "));
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
@ -65,7 +67,7 @@ fn print_config() {
);
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn rustfmt_help() {
assert_that!(&["--", "--help"], contains("Format Rust code"));
@ -73,7 +75,7 @@ fn rustfmt_help() {
assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn cargo_fmt_out_of_line_test_modules() {
// See also https://github.com/rust-lang/rustfmt/issues/5119
@ -96,3 +98,22 @@ fn cargo_fmt_out_of_line_test_modules() {
assert!(stdout.contains(&format!("Diff in {}", path.display())))
}
}
#[rustfmt_only_ci_test]
#[test]
fn cargo_fmt_emits_error_on_line_overflow_true() {
// See also https://github.com/rust-lang/rustfmt/issues/3164
let args = [
"--check",
"--manifest-path",
"tests/cargo-fmt/source/issue_3164/Cargo.toml",
"--",
"--config",
"error_on_line_overflow=true",
];
let (_stdout, stderr) = cargo_fmt(&args);
assert!(stderr.contains(
"line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
))
}

View file

@ -0,0 +1,8 @@
[package]
name = "issue_3164"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,13 @@
#[allow(unused_macros)]
macro_rules! foo {
($id:ident) => {
macro_rules! bar {
($id2:tt) => {
#[cfg(any(target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2))]
fn $id() {}
};
}
};
}
fn main() {}

View file

@ -3,7 +3,7 @@ comment_width = 80
tab_spaces = 2
newline_style = "Unix"
brace_style = "SameLineWhere"
fn_args_layout = "Tall"
fn_params_layout = "Tall"
trailing_comma = "Vertical"
indent_style = "Block"
reorder_imports = false

View file

@ -9,7 +9,7 @@ The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name.
* mod g;
Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs',
so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that
so we should fall back to looking for './lib/c/e.rs', which correctly finds the module, that
rustfmt should format.
'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able

View file

@ -9,7 +9,7 @@ The directory name './lib' conflicts with the './lib.rs' file name.
* mod c;
Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs',
so we should fall back to looking for './a.rs', which correctly finds the modlue that
so we should fall back to looking for './a.rs', which correctly finds the module that
rustfmt should format.
'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able

View file

@ -5,6 +5,8 @@ use std::fs::remove_file;
use std::path::Path;
use std::process::Command;
use rustfmt_config_proc_macro::rustfmt_only_ci_test;
/// Run the rustfmt executable and return its output.
fn rustfmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
@ -47,7 +49,7 @@ macro_rules! assert_that {
};
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
@ -76,7 +78,7 @@ fn print_config() {
remove_file("minimal-config").unwrap();
}
#[ignore]
#[rustfmt_only_ci_test]
#[test]
fn inline_config() {
// single invocation
@ -157,3 +159,18 @@ fn mod_resolution_error_path_attribute_does_not_exist() {
// The path attribute points to a file that does not exist
assert!(stderr.contains("does_not_exist.rs does not exist"));
}
#[test]
fn rustfmt_emits_error_on_line_overflow_true() {
// See also https://github.com/rust-lang/rustfmt/issues/3164
let args = [
"--config",
"error_on_line_overflow=true",
"tests/cargo-fmt/source/issue_3164/src/main.rs",
];
let (_stdout, stderr) = rustfmt(&args);
assert!(stderr.contains(
"line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
))
}

View file

@ -329,7 +329,7 @@ pub enum Feature {
tbm,
/// POPCNT (Population Count)
popcnt,
/// FXSR (Floating-point context fast save and restor)
/// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,

View file

@ -0,0 +1,140 @@
impl Default for WhitespaceCharacters {
fn default() -> Self {
Self {
space: '·', // U+00B7
nbsp: '', // U+237D
tab: '', // U+2192
newline: '', // U+23CE
}
}
}
const RAINBOWS: &[&str] = &[
"rаinЬοѡ", // hue: 0
"raіnЬοw", // hue: 2
"rаіɴЬow", // hue: 2
"raіɴЬoѡ", // hue: 8
"ʀainЬow", // hue: 8
"ʀaіɴboѡ", // hue: 8
"ʀаіnbοw", // hue: 11
"rainЬoѡ", // hue: 14
"raіɴbow", // hue: 14
"rаЬow", // hue: 20
"raіnЬow", // hue: 26
"ʀaiɴbοw", // hue: 32
"raіɴboѡ", // hue: 35
"rаiɴbow", // hue: 35
"rаіnbοw", // hue: 38
"rаinЬow", // hue: 47
"ʀaіnboѡ", // hue: 47
"ʀaіnЬoѡ", // hue: 47
"ʀаіɴbοw", // hue: 53
"ʀaіnЬοѡ", // hue: 57
"raiɴЬoѡ", // hue: 68
"ʀainbοѡ", // hue: 68
"ʀаinboѡ", // hue: 68
"ʀаiɴbοw", // hue: 68
"ʀаіnbow", // hue: 68
"rаіnЬοѡ", // hue: 69
"ʀainЬοw", // hue: 71
"raiɴbow", // hue: 73
"raіnЬoѡ", // hue: 74
"rаіɴbοw", // hue: 77
"raіnЬοѡ", // hue: 81
"raiɴЬow", // hue: 83
"ʀainbοw", // hue: 83
"ʀаinbow", // hue: 83
"ʀаiɴbοѡ", // hue: 83
"ʀаіnboѡ", // hue: 83
"ʀаіɴЬοѡ", // hue: 84
"rainЬow", // hue: 85
"ʀаЬοw", // hue: 86
"ʀаіnbοѡ", // hue: 89
"ʀаіnЬοw", // hue: 92
"rаiɴbοw", // hue: 95
"ʀаіɴbοѡ", // hue: 98
"ʀаЬοѡ", // hue: 99
"raіnbοw", // hue: 101
"ʀаіɴЬοw", // hue: 101
"ʀaiɴboѡ", // hue: 104
"ʀаinbοѡ", // hue: 104
"rаiɴbοѡ", // hue: 107
"ʀаinЬοw", // hue: 107
"rаЬοw", // hue: 110
"rаіnboѡ", // hue: 110
"rаіnbοѡ", // hue: 113
"ʀainЬοѡ", // hue: 114
"rаіnЬοw", // hue: 116
"ʀaіɴЬow", // hue: 116
"rаinbοw", // hue: 122
"ʀаіɴboѡ", // hue: 125
"rаinbοѡ", // hue: 131
"rainbow", // hue: 134
"rаinЬοw", // hue: 134
"ʀаiɴboѡ", // hue: 140
"rainЬοѡ", // hue: 141
"raіɴЬow", // hue: 143
"ʀainЬoѡ", // hue: 143
"ʀaіɴbow", // hue: 143
"ʀainbow", // hue: 148
"rаіɴboѡ", // hue: 149
"ʀainboѡ", // hue: 155
"ʀaіnbow", // hue: 155
"ʀaіnЬow", // hue: 155
"raiɴbοw", // hue: 158
"ʀаЬoѡ", // hue: 158
"rainbοw", // hue: 160
"rаinbow", // hue: 160
"ʀaіɴbοѡ", // hue: 164
"ʀаiɴbow", // hue: 164
"ʀаіnЬoѡ", // hue: 164
"ʀaiɴЬοѡ", // hue: 165
"rаiɴboѡ", // hue: 167
"ʀaіɴЬοw", // hue: 167
"ʀaіɴЬοѡ", // hue: 171
"raіnboѡ", // hue: 173
"ʀаіɴЬoѡ", // hue: 173
"rаіɴbοѡ", // hue: 176
"ʀаinЬow", // hue: 176
"rаЬοѡ", // hue: 177
"rаіɴЬοw", // hue: 179
"ʀаinЬoѡ", // hue: 179
"ʀаіɴbow", // hue: 179
"rаЬoѡ", // hue: 182
"raіɴbοѡ", // hue: 188
"rаіnЬoѡ", // hue: 188
"raiɴЬοѡ", // hue: 189
"raіɴЬοw", // hue: 191
"ʀaіɴbοw", // hue: 191
"ʀаіnЬow", // hue: 191
"rainbοѡ", // hue: 194
"rаinboѡ", // hue: 194
"rаіnbow", // hue: 194
"rainЬοw", // hue: 197
"rаinЬoѡ", // hue: 206
"rаіɴbow", // hue: 206
"rаіɴЬοѡ", // hue: 210
"ʀaiɴЬow", // hue: 212
"raіɴbοw", // hue: 218
"rаіnЬow", // hue: 218
"ʀaiɴbοѡ", // hue: 221
"ʀaiɴЬοw", // hue: 224
"ʀaіnbοѡ", // hue: 227
"raiɴboѡ", // hue: 230
"ʀaіnbοw", // hue: 230
"ʀaіnЬοw", // hue: 230
"ʀаinЬοѡ", // hue: 231
"rainboѡ", // hue: 232
"raіnbow", // hue: 232
"ʀаіɴЬow", // hue: 233
"ʀaіɴЬoѡ", // hue: 239
"ʀаіnЬοѡ", // hue: 246
"raiɴbοѡ", // hue: 248
"ʀаЬow", // hue: 248
"raіɴЬοѡ", // hue: 249
"raiɴЬοw", // hue: 251
"rаіɴЬoѡ", // hue: 251
"ʀaiɴbow", // hue: 251
"ʀаinbοw", // hue: 251
"raіnbοѡ", // hue: 254
];

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Compressed
// rustfmt-fn_params_layout: Compressed
// Function arguments density
trait Lorem {

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Tall
// rustfmt-fn_params_layout: Tall
// Function arguments density
trait Lorem {

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// Function arguments density
trait Lorem {

View file

@ -36,7 +36,7 @@ enum StructLikeVariants {
Normal(u32, String, ),
StructLike { x: i32, // Test comment
// Pre-comment
#[Attr50] y: SomeType, // Aanother Comment
#[Attr50] y: SomeType, // Another Comment
}, SL { a: A }
}

View file

@ -1,5 +1,5 @@
// rustfmt-normalize_comments: true
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Compressed
// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar(

View file

@ -0,0 +1,26 @@
// rustfmt-format_macro_bodies: true
// with comments
macro_rules! macros {
() => {{
Struct {
field: (
42 + //comment 1
42
//comment 2
),
};
}};
}
// without comments
macro_rules! macros {
() => {{
Struct {
field: (
42 +
42
),
};
}};
}

View file

@ -0,0 +1,23 @@
// output doesn't get corrupted when using comments within generic type parameters of a trait
pub trait Something<
A,
// some comment
B,
C
> {
fn a(&self, x: A) -> i32;
fn b(&self, x: B) -> i32;
fn c(&self, x: C) -> i32;
}
pub trait SomethingElse<
A,
/* some comment */
B,
C
> {
fn a(&self, x: A) -> i32;
fn b(&self, x: B) -> i32;
fn c(&self, x: C) -> i32;
}

View file

@ -0,0 +1,149 @@
// rustfmt-version: One
// Based on the issue description
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write1 + fmt::Write2
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write + Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write + Printer1<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + Printer2<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
// Some test cases to ensure other cases formatting were not changed
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
>,
> {
}
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
> + fmt::Write1
+ fmt::Write2,
> {
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
)
{
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
) + fmt::Write
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: for<> FnMut(
&mut ProbeContext<>,
ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
),
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: for<> FnMut(
&mut ProbeContext<>,
ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
) + fmt::Write,
{
}
fn build_sorted_static_get_entry_names(
mut entries: entryyyyyyyy,
) -> (
impl Fn(
AlphabeticalTraversal,
Seconddddddddddddddddddddddddddddddddddd
) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ Sendddddddddddddddddddddddddddddddddddddddddddd
) {
}
pub trait SomeTrait:
Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
{
}
trait B = where
for<'b> &'b Self: Send
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;

View file

@ -0,0 +1,149 @@
// rustfmt-version: Two
// Based on the issue description
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write1 + fmt::Write2
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write + Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write + Printer1<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + Printer2<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
// Some test cases to ensure other cases formatting were not changed
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
>,
> {
}
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
> + fmt::Write1
+ fmt::Write2,
> {
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
)
{
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
) + fmt::Write
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: for<> FnMut(
&mut ProbeContext<>,
ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
),
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: for<> FnMut(
&mut ProbeContext<>,
ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
) + fmt::Write,
{
}
fn build_sorted_static_get_entry_names(
mut entries: entryyyyyyyy,
) -> (
impl Fn(
AlphabeticalTraversal,
Seconddddddddddddddddddddddddddddddddddd
) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ Sendddddddddddddddddddddddddddddddddddddddddddd
) {
}
pub trait SomeTrait:
Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
{
}
trait B = where
for<'b> &'b Self: Send
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;

View file

@ -0,0 +1,29 @@
// rustfmt-max_width: 160
// rustfmt-fn_call_width: 96
// rustfmt-fn_args_layout: Compressed
// rustfmt-trailing_comma: Always
// rustfmt-wrap_comments: true
fn foo() {
for elem in try!(gen_epub_book::ops::parse_descriptor_file(&mut try!(File::open(&opts.source_file.1).map_err(|_| {
gen_epub_book::Error::Io {
desc: "input file",
op: "open",
more: None,
}
})),
"input file")) {
println!("{}", elem);
}
}
fn write_content() {
io::copy(try!(File::open(in_f).map_err(|_| {
Error::Io {
desc: "Content",
op: "open",
more: None,
}
})),
w);
}

View file

@ -0,0 +1,4 @@
fn main() {
let x = 1;
;let y = 3;
}

View file

@ -0,0 +1,6 @@
fn main() {;7
}
fn main() {
;7
}

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["*"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should skip this invocation
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["*","items"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should also skip this invocation, as the wildcard covers it
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: []
// Should not skip this invocation
items!(
const _: u8 = 0;
);
// Should not skip this invocation
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["items"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should not skip this invocation
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,6 @@
// rustfmt-skip_macro_invocations: ["unknown"]
// Should not skip this invocation
items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,16 @@
// rustfmt-skip_macro_invocations: ["foo","bar"]
// Should skip this invocation
foo!(
const _: u8 = 0;
);
// Should skip this invocation
bar!(
const _: u8 = 0;
);
// Should not skip this invocation
baz!(
const _: u8 = 0;
);

View file

@ -0,0 +1,6 @@
// rustfmt-skip_macro_invocations: ["items"]
// Should not skip this invocation
self::items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,6 @@
// rustfmt-skip_macro_invocations: ["self::items"]
// Should skip this invocation
self::items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,6 @@
// rustfmt-skip_macro_invocations: ["self::items"]
// Should not skip this invocation
items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,32 @@
// rustfmt-skip_macro_invocations: ["aaa","ccc"]
// These tests demonstrate a realistic use case with use aliases.
// The use statements should not impact functionality in any way.
use crate::{aaa, bbb, ddd};
// No use alias, invocation in list
// Should skip this invocation
aaa!(
const _: u8 = 0;
);
// Use alias, invocation in list
// Should skip this invocation
use crate::bbb as ccc;
ccc!(
const _: u8 = 0;
);
// Use alias, invocation not in list
// Should not skip this invocation
use crate::ddd as eee;
eee!(
const _: u8 = 0;
);
// No use alias, invocation not in list
// Should not skip this invocation
fff!(
const _: u8 = 0;
);

View file

@ -1,4 +1,4 @@
// Test tuple litterals
// Test tuple literals
fn foo() {
let a = (a, a, a, a, a);

View file

@ -11,6 +11,6 @@
///
fn foo() {}
/// A long commment for wrapping
/// A long comment for wrapping
/// This is a long long long long long long long long long long long long long long long long long long long long sentence.
fn bar() {}

View file

@ -314,7 +314,7 @@ pub enum Feature {
tbm,
/// POPCNT (Population Count)
popcnt,
/// FXSR (Floating-point context fast save and restor)
/// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,

View file

@ -0,0 +1,140 @@
impl Default for WhitespaceCharacters {
fn default() -> Self {
Self {
space: '·', // U+00B7
nbsp: '', // U+237D
tab: '', // U+2192
newline: '', // U+23CE
}
}
}
const RAINBOWS: &[&str] = &[
"rаinЬοѡ", // hue: 0
"raіnЬοw", // hue: 2
"rаіɴЬow", // hue: 2
"raіɴЬoѡ", // hue: 8
"ʀainЬow", // hue: 8
"ʀaіɴboѡ", // hue: 8
"ʀаіnbοw", // hue: 11
"rainЬoѡ", // hue: 14
"raіɴbow", // hue: 14
"rаЬow", // hue: 20
"raіnЬow", // hue: 26
"ʀaiɴbοw", // hue: 32
"raіɴboѡ", // hue: 35
"rаiɴbow", // hue: 35
"rаіnbοw", // hue: 38
"rаinЬow", // hue: 47
"ʀaіnboѡ", // hue: 47
"ʀaіnЬoѡ", // hue: 47
"ʀаіɴbοw", // hue: 53
"ʀaіnЬοѡ", // hue: 57
"raiɴЬoѡ", // hue: 68
"ʀainbοѡ", // hue: 68
"ʀаinboѡ", // hue: 68
"ʀаiɴbοw", // hue: 68
"ʀаіnbow", // hue: 68
"rаіnЬοѡ", // hue: 69
"ʀainЬοw", // hue: 71
"raiɴbow", // hue: 73
"raіnЬoѡ", // hue: 74
"rаіɴbοw", // hue: 77
"raіnЬοѡ", // hue: 81
"raiɴЬow", // hue: 83
"ʀainbοw", // hue: 83
"ʀаinbow", // hue: 83
"ʀаiɴbοѡ", // hue: 83
"ʀаіnboѡ", // hue: 83
"ʀаіɴЬοѡ", // hue: 84
"rainЬow", // hue: 85
"ʀаЬοw", // hue: 86
"ʀаіnbοѡ", // hue: 89
"ʀаіnЬοw", // hue: 92
"rаiɴbοw", // hue: 95
"ʀаіɴbοѡ", // hue: 98
"ʀаЬοѡ", // hue: 99
"raіnbοw", // hue: 101
"ʀаіɴЬοw", // hue: 101
"ʀaiɴboѡ", // hue: 104
"ʀаinbοѡ", // hue: 104
"rаiɴbοѡ", // hue: 107
"ʀаinЬοw", // hue: 107
"rаЬοw", // hue: 110
"rаіnboѡ", // hue: 110
"rаіnbοѡ", // hue: 113
"ʀainЬοѡ", // hue: 114
"rаіnЬοw", // hue: 116
"ʀaіɴЬow", // hue: 116
"rаinbοw", // hue: 122
"ʀаіɴboѡ", // hue: 125
"rаinbοѡ", // hue: 131
"rainbow", // hue: 134
"rаinЬοw", // hue: 134
"ʀаiɴboѡ", // hue: 140
"rainЬοѡ", // hue: 141
"raіɴЬow", // hue: 143
"ʀainЬoѡ", // hue: 143
"ʀaіɴbow", // hue: 143
"ʀainbow", // hue: 148
"rаіɴboѡ", // hue: 149
"ʀainboѡ", // hue: 155
"ʀaіnbow", // hue: 155
"ʀaіnЬow", // hue: 155
"raiɴbοw", // hue: 158
"ʀаЬoѡ", // hue: 158
"rainbοw", // hue: 160
"rаinbow", // hue: 160
"ʀaіɴbοѡ", // hue: 164
"ʀаiɴbow", // hue: 164
"ʀаіnЬoѡ", // hue: 164
"ʀaiɴЬοѡ", // hue: 165
"rаiɴboѡ", // hue: 167
"ʀaіɴЬοw", // hue: 167
"ʀaіɴЬοѡ", // hue: 171
"raіnboѡ", // hue: 173
"ʀаіɴЬoѡ", // hue: 173
"rаіɴbοѡ", // hue: 176
"ʀаinЬow", // hue: 176
"rаЬοѡ", // hue: 177
"rаіɴЬοw", // hue: 179
"ʀаinЬoѡ", // hue: 179
"ʀаіɴbow", // hue: 179
"rаЬoѡ", // hue: 182
"raіɴbοѡ", // hue: 188
"rаіnЬoѡ", // hue: 188
"raiɴЬοѡ", // hue: 189
"raіɴЬοw", // hue: 191
"ʀaіɴbοw", // hue: 191
"ʀаіnЬow", // hue: 191
"rainbοѡ", // hue: 194
"rаinboѡ", // hue: 194
"rаіnbow", // hue: 194
"rainЬοw", // hue: 197
"rаinЬoѡ", // hue: 206
"rаіɴbow", // hue: 206
"rаіɴЬοѡ", // hue: 210
"ʀaiɴЬow", // hue: 212
"raіɴbοw", // hue: 218
"rаіnЬow", // hue: 218
"ʀaiɴbοѡ", // hue: 221
"ʀaiɴЬοw", // hue: 224
"ʀaіnbοѡ", // hue: 227
"raiɴboѡ", // hue: 230
"ʀaіnbοw", // hue: 230
"ʀaіnЬοw", // hue: 230
"ʀаinЬοѡ", // hue: 231
"rainboѡ", // hue: 232
"raіnbow", // hue: 232
"ʀаіɴЬow", // hue: 233
"ʀaіɴЬoѡ", // hue: 239
"ʀаіnЬοѡ", // hue: 246
"raiɴbοѡ", // hue: 248
"ʀаЬow", // hue: 248
"raіɴЬοѡ", // hue: 249
"raiɴЬοw", // hue: 251
"rаіɴЬoѡ", // hue: 251
"ʀaiɴbow", // hue: 251
"ʀаinbοw", // hue: 251
"raіnbοѡ", // hue: 254
];

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Compressed
// rustfmt-fn_params_layout: Compressed
// Function arguments density
trait Lorem {

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Tall
// rustfmt-fn_params_layout: Tall
// Function arguments density
trait Lorem {

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// Function arguments density
trait Lorem {

View file

@ -43,7 +43,7 @@ enum StructLikeVariants {
x: i32, // Test comment
// Pre-comment
#[Attr50]
y: SomeType, // Aanother Comment
y: SomeType, // Another Comment
},
SL {
a: A,

View file

@ -1,5 +1,5 @@
// rustfmt-normalize_comments: true
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Compressed
// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.

View file

@ -1,4 +1,4 @@
// rustfmt-fn_args_layout: Vertical
// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar() -> u8 {

View file

@ -0,0 +1,6 @@
// rustfmt-format_macro_matchers: false
macro_rules! foo {
($a:ident : $b:ty) => {};
($a:ident $b:ident $c:ident) => {};
}

View file

@ -0,0 +1,6 @@
// rustfmt-format_macro_matchers: true
macro_rules! foo {
($a:ident : $b:ty) => {};
($a:ident $b:ident $c:ident) => {};
}

View file

@ -0,0 +1,26 @@
// rustfmt-format_macro_bodies: false
// with comments
macro_rules! macros {
() => {{
Struct {
field: (
42 + //comment 1
42
//comment 2
),
};
}};
}
// without comments
macro_rules! macros {
() => {{
Struct {
field: (
42 +
42
),
};
}};
}

View file

@ -0,0 +1,21 @@
// rustfmt-format_macro_bodies: true
// with comments
macro_rules! macros {
() => {{
Struct {
field: (
42 + //comment 1
42
//comment 2
),
};
}};
}
// without comments
macro_rules! macros {
() => {{
Struct { field: (42 + 42) };
}};
}

View file

@ -0,0 +1,19 @@
// output doesn't get corrupted when using comments within generic type parameters of a trait
pub trait Something<
A,
// some comment
B,
C,
>
{
fn a(&self, x: A) -> i32;
fn b(&self, x: B) -> i32;
fn c(&self, x: C) -> i32;
}
pub trait SomethingElse<A, /* some comment */ B, C> {
fn a(&self, x: A) -> i32;
fn b(&self, x: B) -> i32;
fn c(&self, x: C) -> i32;
}

View file

@ -0,0 +1,150 @@
// rustfmt-version: One
// Based on the issue description
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write1
+ fmt::Write2
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write
+ Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write
+ Printer1<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + Printer2<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
// Some test cases to ensure other cases formatting were not changed
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
>,
> {
}
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
> + fmt::Write1
+ fmt::Write2,
> {
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
),
{
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
) + fmt::Write,
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: FnMut(
&mut ProbeContext,
ty::PolyTraitRefffffffffffffffffffffffffffffffff,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
),
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: FnMut(
&mut ProbeContext,
ty::PolyTraitRefffffffffffffffffffffffffffffffff,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
) + fmt::Write,
{
}
fn build_sorted_static_get_entry_names(
mut entries: entryyyyyyyy,
) -> (impl Fn(
AlphabeticalTraversal,
Seconddddddddddddddddddddddddddddddddddd,
) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ Sendddddddddddddddddddddddddddddddddddddddddddd) {
}
pub trait SomeTrait:
Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
{
}
trait B = where
for<'b> &'b Self: Send
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;

View file

@ -0,0 +1,152 @@
// rustfmt-version: Two
// Based on the issue description
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write
{
//
}
pub trait PrettyPrinter<'tcx>:
Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + fmt::Write1
+ fmt::Write2
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write
+ Printer<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
pub trait PrettyPrinter<'tcx>:
fmt::Write
+ Printer1<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
> + Printer2<
'tcx,
Error = fmt::Error,
Path = Self,
Region = Self,
Type = Self,
DynExistential = Self,
Const = Self,
>
{
//
}
// Some test cases to ensure other cases formatting were not changed
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
>,
> {
}
fn f() -> Box<
FnMut() -> Thing<
WithType = LongItemName,
Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
> + fmt::Write1
+ fmt::Write2,
> {
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
),
{
}
fn foo<F>(foo2: F)
where
F: Fn(
// this comment is deleted
) + fmt::Write,
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: FnMut(
&mut ProbeContext,
ty::PolyTraitRefffffffffffffffffffffffffffffffff,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
),
{
}
fn elaborate_bounds<F>(mut mk_cand: F)
where
F: FnMut(
&mut ProbeContext,
ty::PolyTraitRefffffffffffffffffffffffffffffffff,
tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
) + fmt::Write,
{
}
fn build_sorted_static_get_entry_names(
mut entries: entryyyyyyyy,
) -> (
impl Fn(
AlphabeticalTraversal,
Seconddddddddddddddddddddddddddddddddddd,
) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ Sendddddddddddddddddddddddddddddddddddddddddddd
) {
}
pub trait SomeTrait:
Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
{
}
trait B = where
for<'b> &'b Self: Send
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;

View file

@ -1,7 +1,7 @@
// rustfmt-brace_style: SameLineWhere
// rustfmt-comment_width: 100
// rustfmt-edition: 2018
// rustfmt-fn_args_layout: Compressed
// rustfmt-fn_params_layout: Compressed
// rustfmt-hard_tabs: false
// rustfmt-match_block_trailing_comma: true
// rustfmt-max_width: 100

View file

@ -0,0 +1,4 @@
// Test /* comment */ inside trait generics does not get duplicated.
trait Test</* comment */ T> {}
trait TestTwo</* comment */ T, /* comment */ V> {}

View file

@ -0,0 +1,33 @@
// rustfmt-max_width: 160
// rustfmt-fn_call_width: 96
// rustfmt-fn_args_layout: Compressed
// rustfmt-trailing_comma: Always
// rustfmt-wrap_comments: true
fn foo() {
for elem in try!(gen_epub_book::ops::parse_descriptor_file(
&mut try!(File::open(&opts.source_file.1).map_err(|_| {
gen_epub_book::Error::Io {
desc: "input file",
op: "open",
more: None,
}
})),
"input file"
)) {
println!("{}", elem);
}
}
fn write_content() {
io::copy(
try!(File::open(in_f).map_err(|_| {
Error::Io {
desc: "Content",
op: "open",
more: None,
}
})),
w,
);
}

View file

@ -0,0 +1,2 @@
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding::
BluetoothRemoteGATTServerMethods;

View file

@ -0,0 +1,4 @@
fn main() {
let x = 1;
let y = 3;
}

View file

@ -0,0 +1,7 @@
fn main() {
7
}
fn main() {
7
}

View file

@ -0,0 +1,13 @@
//rustfmt-format_macro_bodies: true
macro_rules! mto_text_left {
($buf:ident, $n:ident, $pos:ident, $state:ident) => {{
let cursor = loop {
state = match iter.next() {
None if $pos == DP::Start => break last_char_idx($buf),
None /*some comment */ => break 0,
};
};
Ok(saturate_cursor($buf, cursor))
}};
}

View file

@ -0,0 +1,8 @@
type Foo = impl Send;
struct Struct<
const C: usize = {
let _: Foo = ();
//~^ ERROR: mismatched types
0
},
>;

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["*"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should skip this invocation
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["*","items"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should also skip this invocation, as the wildcard covers it
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: []
// Should not skip this invocation
items!(
const _: u8 = 0;
);
// Should not skip this invocation
renamed_items!(
const _: u8 = 0;
);

View file

@ -0,0 +1,11 @@
// rustfmt-skip_macro_invocations: ["items"]
// Should skip this invocation
items!(
const _: u8 = 0;
);
// Should not skip this invocation
renamed_items!(
const _: u8 = 0;
);

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