Merge remote-tracking branch 'upstream/master' into subtree-sync-2022-08-06

This commit is contained in:
Caleb Cartwright 2022-08-06 19:07:02 -05:00
commit a392dd10cd
93 changed files with 1361 additions and 153 deletions

View File

@ -840,7 +840,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

2
Cargo.lock generated
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",

View File

@ -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:
@ -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`

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

@ -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

@ -337,7 +337,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);
@ -391,7 +391,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

@ -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<'_>,
@ -498,6 +552,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> {
@ -507,6 +563,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,
}
}
@ -519,6 +577,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:
//
@ -731,22 +797,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(
@ -810,15 +866,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..];
@ -829,18 +884,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,
}
}

118
src/config/macro_names.rs Normal file
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,6 +13,8 @@ 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]
@ -22,6 +24,7 @@ pub(crate) mod options;
pub(crate) mod file_lines;
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 +70,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 +124,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 +182,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 +198,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 +411,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 enum PartiallyUnstableOption {
V1,
V2,
#[unstable_variant]
V3,
}
create_config! {
// Options that are used by the generated functions
@ -427,6 +445,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 +475,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 +570,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 +583,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 +596,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 +626,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 +653,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 +1007,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;
@ -2043,8 +2043,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(
@ -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

@ -3,7 +3,6 @@
#![warn(unreachable_pub)]
#![recursion_limit = "256"]
#![allow(clippy::match_like_matches_macro)]
#![allow(unreachable_pub)]
#[macro_use]
extern crate derive_new;

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

@ -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

@ -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,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

@ -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

@ -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,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

@ -10,7 +10,7 @@
/// ```
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() {}