about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--Configurations.md176
-rw-r--r--Processes.md2
-rwxr-xr-xci/build_and_test.bat1
-rwxr-xr-xci/build_and_test.sh1
-rw-r--r--config_proc_macro/Cargo.lock2
-rw-r--r--config_proc_macro/Cargo.toml2
-rw-r--r--config_proc_macro/src/attrs.rs27
-rw-r--r--config_proc_macro/src/item_enum.rs40
-rw-r--r--config_proc_macro/src/lib.rs13
-rw-r--r--config_proc_macro/tests/smoke.rs1
-rw-r--r--src/attr.rs4
-rw-r--r--src/bin/main.rs2
-rw-r--r--src/chains.rs120
-rw-r--r--src/config/config_type.rs122
-rw-r--r--src/config/macro_names.rs118
-rw-r--r--src/config/mod.rs147
-rw-r--r--src/expr.rs9
-rw-r--r--src/imports.rs4
-rw-r--r--src/items.rs8
-rw-r--r--src/lib.rs1
-rw-r--r--src/macros.rs18
-rw-r--r--src/skip.rs79
-rw-r--r--src/test/configuration_snippet.rs7
-rw-r--r--src/test/mod.rs6
-rw-r--r--src/utils.rs5
-rw-r--r--src/visitor.rs13
-rw-r--r--tests/cargo-fmt/main.rs29
-rw-r--r--tests/cargo-fmt/source/issue_3164/Cargo.toml8
-rw-r--r--tests/cargo-fmt/source/issue_3164/src/main.rs13
-rw-r--r--tests/config/small_tabs.toml2
-rw-r--r--tests/mod-resolver/issue-5198/lib/c/d/explanation.txt2
-rw-r--r--tests/mod-resolver/issue-5198/lib/explanation.txt2
-rw-r--r--tests/rustfmt/main.rs21
-rw-r--r--tests/source/cfg_if/detect/arch/x86.rs2
-rw-r--r--tests/source/configs/fn_params_layout/compressed.rs (renamed from tests/source/configs/fn_args_layout/compressed.rs)2
-rw-r--r--tests/source/configs/fn_params_layout/tall.rs (renamed from tests/source/configs/fn_args_layout/tall.rs)2
-rw-r--r--tests/source/configs/fn_params_layout/vertical.rs (renamed from tests/source/configs/fn_args_layout/vertical.rs)2
-rw-r--r--tests/source/enum.rs2
-rw-r--r--tests/source/fn-custom-7.rs2
-rw-r--r--tests/source/fn-custom.rs2
-rw-r--r--tests/source/fn_args_layout-vertical.rs2
-rw-r--r--tests/source/issue-3987/format_macro_bodies_true.rs26
-rw-r--r--tests/source/issue-4643.rs23
-rw-r--r--tests/source/issue_1306.rs29
-rw-r--r--tests/source/issue_3245.rs4
-rw-r--r--tests/source/issue_3561.rs6
-rw-r--r--tests/source/skip_macro_invocations/all.rs11
-rw-r--r--tests/source/skip_macro_invocations/all_and_name.rs11
-rw-r--r--tests/source/skip_macro_invocations/empty.rs11
-rw-r--r--tests/source/skip_macro_invocations/name.rs11
-rw-r--r--tests/source/skip_macro_invocations/name_unknown.rs6
-rw-r--r--tests/source/skip_macro_invocations/names.rs16
-rw-r--r--tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs6
-rw-r--r--tests/source/skip_macro_invocations/path_qualified_match.rs6
-rw-r--r--tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs6
-rw-r--r--tests/source/skip_macro_invocations/use_alias_examples.rs32
-rw-r--r--tests/source/tuple.rs2
-rw-r--r--tests/source/wrap_comments_should_not_imply_format_doc_comments.rs2
-rw-r--r--tests/target/cfg_if/detect/arch/x86.rs2
-rw-r--r--tests/target/configs/fn_params_layout/compressed.rs (renamed from tests/target/configs/fn_args_layout/compressed.rs)2
-rw-r--r--tests/target/configs/fn_params_layout/tall.rs (renamed from tests/target/configs/fn_args_layout/tall.rs)2
-rw-r--r--tests/target/configs/fn_params_layout/vertical.rs (renamed from tests/target/configs/fn_args_layout/vertical.rs)2
-rw-r--r--tests/target/enum.rs2
-rw-r--r--tests/target/fn-custom-7.rs2
-rw-r--r--tests/target/fn-custom.rs2
-rw-r--r--tests/target/fn_args_layout-vertical.rs2
-rw-r--r--tests/target/issue-2534/format_macro_matchers_false.rs6
-rw-r--r--tests/target/issue-2534/format_macro_matchers_true.rs6
-rw-r--r--tests/target/issue-3987/format_macro_bodies_false.rs26
-rw-r--r--tests/target/issue-3987/format_macro_bodies_true.rs21
-rw-r--r--tests/target/issue-4643.rs19
-rw-r--r--tests/target/issue-4791/issue_4928.rs2
-rw-r--r--tests/target/issue-5358.rs4
-rw-r--r--tests/target/issue_1306.rs33
-rw-r--r--tests/target/issue_3033.rs2
-rw-r--r--tests/target/issue_3245.rs4
-rw-r--r--tests/target/issue_3561.rs7
-rw-r--r--tests/target/issue_4350.rs13
-rw-r--r--tests/target/skip_macro_invocations/all.rs11
-rw-r--r--tests/target/skip_macro_invocations/all_and_name.rs11
-rw-r--r--tests/target/skip_macro_invocations/empty.rs11
-rw-r--r--tests/target/skip_macro_invocations/name.rs11
-rw-r--r--tests/target/skip_macro_invocations/name_unknown.rs6
-rw-r--r--tests/target/skip_macro_invocations/names.rs16
-rw-r--r--tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs6
-rw-r--r--tests/target/skip_macro_invocations/path_qualified_match.rs6
-rw-r--r--tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs6
-rw-r--r--tests/target/skip_macro_invocations/use_alias_examples.rs32
-rw-r--r--tests/target/tuple.rs2
-rw-r--r--tests/target/wrap_comments_should_not_imply_format_doc_comments.rs2
93 files changed, 1361 insertions, 153 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c1893bf8c3..e9ce930dabc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/Cargo.lock b/Cargo.lock
index 311df226da1..e5175528970 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -476,7 +476,7 @@ checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
 
 [[package]]
 name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index 7a4e02d69ed..7438335eaa7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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`
diff --git a/Configurations.md b/Configurations.md
index 8b96b9d3689..e066553dc63 100644
--- a/Configurations.md
+++ b/Configurations.md
@@ -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`
 
diff --git a/Processes.md b/Processes.md
index 9d86d52b122..334414702b1 100644
--- a/Processes.md
+++ b/Processes.md
@@ -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
 
diff --git a/ci/build_and_test.bat b/ci/build_and_test.bat
index ef41017783f..69dae1fff7b 100755
--- a/ci/build_and_test.bat
+++ b/ci/build_and_test.bat
@@ -1,4 +1,5 @@
 set "RUSTFLAGS=-D warnings"
+set "RUSTFMT_CI=1"
 
 :: Print version information
 rustc -Vv || exit /b 1
diff --git a/ci/build_and_test.sh b/ci/build_and_test.sh
index 8fa0f67b0d0..94991853263 100755
--- a/ci/build_and_test.sh
+++ b/ci/build_and_test.sh
@@ -3,6 +3,7 @@
 set -euo pipefail
 
 export RUSTFLAGS="-D warnings"
+export RUSTFMT_CI=1
 
 # Print version information
 rustc -Vv
diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock
index ecf561f28fb..49f2f72a8d2 100644
--- a/config_proc_macro/Cargo.lock
+++ b/config_proc_macro/Cargo.lock
@@ -22,7 +22,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml
index a41b3a5e6bf..d10d0469cc4 100644
--- a/config_proc_macro/Cargo.toml
+++ b/config_proc_macro/Cargo.toml
@@ -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"
diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs
index 0baba046f9e..dd18ff572cb 100644
--- a/config_proc_macro/src/attrs.rs
+++ b/config_proc_macro/src/attrs.rs
@@ -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 {
diff --git a/config_proc_macro/src/item_enum.rs b/config_proc_macro/src/item_enum.rs
index dcee77a8549..731a7ea0607 100644
--- a/config_proc_macro/src/item_enum.rs
+++ b/config_proc_macro/src/item_enum.rs
@@ -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;
diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs
index e772c53f423..0c54c132c97 100644
--- a/config_proc_macro/src/lib.rs
+++ b/config_proc_macro/src/lib.rs
@@ -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
+    }
+}
diff --git a/config_proc_macro/tests/smoke.rs b/config_proc_macro/tests/smoke.rs
index 940a8a0c251..c8a83e39c9e 100644
--- a/config_proc_macro/tests/smoke.rs
+++ b/config_proc_macro/tests/smoke.rs
@@ -1,6 +1,7 @@
 pub mod config {
     pub trait ConfigType: Sized {
         fn doc_hint() -> String;
+        fn stable_variant(&self) -> bool;
     }
 }
 
diff --git a/src/attr.rs b/src/attr.rs
index 41ba9a847e6..b1efaa21f27 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -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
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 8e871e61f26..be64559e877 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -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(
         "",
diff --git a/src/chains.rs b/src/chains.rs
index e26e24ec55a..a2976bbe92a 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -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(
diff --git a/src/config/config_type.rs b/src/config/config_type.rs
index e37ed798cb5..90a67e12c5a 100644
--- a/src/config/config_type.rs
+++ b/src/config/config_type.rs
@@ -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,
+    }
+}
diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs
new file mode 100644
index 00000000000..26ad78d6dca
--- /dev/null
+++ b/src/config/macro_names.rs
@@ -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");
+    }
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index f49c18d3a46..4ec7e924bf6 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -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()))
+            ])
+        );
+    }
 }
diff --git a/src/expr.rs b/src/expr.rs
index a7b73ba78c5..c9d704765c2 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -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))
                 }
diff --git a/src/imports.rs b/src/imports.rs
index 8d41c881589..140d8953abc 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -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))
diff --git a/src/items.rs b/src/items.rs
index 8f35068e35f..755a41f6bf0 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -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,
diff --git a/src/lib.rs b/src/lib.rs
index 1d1ef525f23..495010a297d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;
diff --git a/src/macros.rs b/src/macros.rs
index 3a641fab5d6..1bcb7774c94 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -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),
diff --git a/src/skip.rs b/src/skip.rs
index 0fdc097efc2..59d6d84c964 100644
--- a/src/skip.rs
+++ b/src/skip.rs
@@ -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;
     }
 }
 
diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs
index c8fda7c8556..c70b3c5facd 100644
--- a/src/test/configuration_snippet.rs
+++ b/src/test/configuration_snippet.rs
@@ -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");
         }
 
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 6b5bc2b30dd..cfad4a8ed0e 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -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
 }
diff --git a/src/utils.rs b/src/utils.rs
index cd852855602..a97bcba0bd2 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -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 {
diff --git a/src/visitor.rs b/src/visitor.rs
index 9a0e0752c12..c0fc37eaaa8 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -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,
         }
     }
 
diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs
index 348876cd264..701c36fadea 100644
--- a/tests/cargo-fmt/main.rs
+++ b/tests/cargo-fmt/main.rs
@@ -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)"
+    ))
+}
diff --git a/tests/cargo-fmt/source/issue_3164/Cargo.toml b/tests/cargo-fmt/source/issue_3164/Cargo.toml
new file mode 100644
index 00000000000..580ef7e6e24
--- /dev/null
+++ b/tests/cargo-fmt/source/issue_3164/Cargo.toml
@@ -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]
diff --git a/tests/cargo-fmt/source/issue_3164/src/main.rs b/tests/cargo-fmt/source/issue_3164/src/main.rs
new file mode 100644
index 00000000000..9330107ac8d
--- /dev/null
+++ b/tests/cargo-fmt/source/issue_3164/src/main.rs
@@ -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() {}
diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml
index c3cfd34317a..4c37100894f 100644
--- a/tests/config/small_tabs.toml
+++ b/tests/config/small_tabs.toml
@@ -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
diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
index 92c9e302143..254102ebabd 100644
--- a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
+++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
@@ -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
diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt
index d436a8076cd..90464def8eb 100644
--- a/tests/mod-resolver/issue-5198/lib/explanation.txt
+++ b/tests/mod-resolver/issue-5198/lib/explanation.txt
@@ -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
diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs
index 4c6d52726f3..7ff301e8019 100644
--- a/tests/rustfmt/main.rs
+++ b/tests/rustfmt/main.rs
@@ -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)"
+    ))
+}
diff --git a/tests/source/cfg_if/detect/arch/x86.rs b/tests/source/cfg_if/detect/arch/x86.rs
index d26f4ee894f..131cbb855f1 100644
--- a/tests/source/cfg_if/detect/arch/x86.rs
+++ b/tests/source/cfg_if/detect/arch/x86.rs
@@ -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,
diff --git a/tests/source/configs/fn_args_layout/compressed.rs b/tests/source/configs/fn_params_layout/compressed.rs
index 66a371c259f..eb573d3121f 100644
--- a/tests/source/configs/fn_args_layout/compressed.rs
+++ b/tests/source/configs/fn_params_layout/compressed.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/source/configs/fn_args_layout/tall.rs b/tests/source/configs/fn_params_layout/tall.rs
index f11e86fd313..4be34f0fe4a 100644
--- a/tests/source/configs/fn_args_layout/tall.rs
+++ b/tests/source/configs/fn_params_layout/tall.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Tall
+// rustfmt-fn_params_layout: Tall
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/source/configs/fn_args_layout/vertical.rs b/tests/source/configs/fn_params_layout/vertical.rs
index a23cc025225..674968023f9 100644
--- a/tests/source/configs/fn_args_layout/vertical.rs
+++ b/tests/source/configs/fn_params_layout/vertical.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/source/enum.rs b/tests/source/enum.rs
index 0ed9651abe7..a7b9616929c 100644
--- a/tests/source/enum.rs
+++ b/tests/source/enum.rs
@@ -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 }
 }
 
diff --git a/tests/source/fn-custom-7.rs b/tests/source/fn-custom-7.rs
index d5330196bf7..3ecd8701727 100644
--- a/tests/source/fn-custom-7.rs
+++ b/tests/source/fn-custom-7.rs
@@ -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.
diff --git a/tests/source/fn-custom.rs b/tests/source/fn-custom.rs
index 77ced4c5e0e..64ef0ecfaae 100644
--- a/tests/source/fn-custom.rs
+++ b/tests/source/fn-custom.rs
@@ -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.
diff --git a/tests/source/fn_args_layout-vertical.rs b/tests/source/fn_args_layout-vertical.rs
index 759bc83d015..fd6e3f0442e 100644
--- a/tests/source/fn_args_layout-vertical.rs
+++ b/tests/source/fn_args_layout-vertical.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
 
 // Empty list should stay on one line.
 fn do_bar(
diff --git a/tests/source/issue-3987/format_macro_bodies_true.rs b/tests/source/issue-3987/format_macro_bodies_true.rs
new file mode 100644
index 00000000000..9af114fbe57
--- /dev/null
+++ b/tests/source/issue-3987/format_macro_bodies_true.rs
@@ -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
+            ),
+        };
+    }};
+}
diff --git a/tests/source/issue-4643.rs b/tests/source/issue-4643.rs
new file mode 100644
index 00000000000..382072d9004
--- /dev/null
+++ b/tests/source/issue-4643.rs
@@ -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;
+}
diff --git a/tests/source/issue_1306.rs b/tests/source/issue_1306.rs
new file mode 100644
index 00000000000..03b78e34108
--- /dev/null
+++ b/tests/source/issue_1306.rs
@@ -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);
+}
diff --git a/tests/source/issue_3245.rs b/tests/source/issue_3245.rs
new file mode 100644
index 00000000000..0279246ed6a
--- /dev/null
+++ b/tests/source/issue_3245.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let x = 1;
+    ;let y = 3;
+}
diff --git a/tests/source/issue_3561.rs b/tests/source/issue_3561.rs
new file mode 100644
index 00000000000..8f6cd8f9fbc
--- /dev/null
+++ b/tests/source/issue_3561.rs
@@ -0,0 +1,6 @@
+fn main() {;7
+}
+
+fn main() {
+    ;7
+}
diff --git a/tests/source/skip_macro_invocations/all.rs b/tests/source/skip_macro_invocations/all.rs
new file mode 100644
index 00000000000..d0437ee10fd
--- /dev/null
+++ b/tests/source/skip_macro_invocations/all.rs
@@ -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;
+);
diff --git a/tests/source/skip_macro_invocations/all_and_name.rs b/tests/source/skip_macro_invocations/all_and_name.rs
new file mode 100644
index 00000000000..1f6722344fe
--- /dev/null
+++ b/tests/source/skip_macro_invocations/all_and_name.rs
@@ -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;
+);
diff --git a/tests/source/skip_macro_invocations/empty.rs b/tests/source/skip_macro_invocations/empty.rs
new file mode 100644
index 00000000000..f3dd89dc4db
--- /dev/null
+++ b/tests/source/skip_macro_invocations/empty.rs
@@ -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;
+);
diff --git a/tests/source/skip_macro_invocations/name.rs b/tests/source/skip_macro_invocations/name.rs
new file mode 100644
index 00000000000..7fa5d3a6f71
--- /dev/null
+++ b/tests/source/skip_macro_invocations/name.rs
@@ -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;
+);
diff --git a/tests/source/skip_macro_invocations/name_unknown.rs b/tests/source/skip_macro_invocations/name_unknown.rs
new file mode 100644
index 00000000000..d5669532524
--- /dev/null
+++ b/tests/source/skip_macro_invocations/name_unknown.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+        const _: u8 = 0;
+);
diff --git a/tests/source/skip_macro_invocations/names.rs b/tests/source/skip_macro_invocations/names.rs
new file mode 100644
index 00000000000..a920381a455
--- /dev/null
+++ b/tests/source/skip_macro_invocations/names.rs
@@ -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;
+);
diff --git a/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs
new file mode 100644
index 00000000000..61296869a50
--- /dev/null
+++ b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+        const _: u8 = 0;
+);
diff --git a/tests/source/skip_macro_invocations/path_qualified_match.rs b/tests/source/skip_macro_invocations/path_qualified_match.rs
new file mode 100644
index 00000000000..9398918a9e1
--- /dev/null
+++ b/tests/source/skip_macro_invocations/path_qualified_match.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+        const _: u8 = 0;
+);
diff --git a/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs
new file mode 100644
index 00000000000..4e3eb542dbe
--- /dev/null
+++ b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+        const _: u8 = 0;
+);
diff --git a/tests/source/skip_macro_invocations/use_alias_examples.rs b/tests/source/skip_macro_invocations/use_alias_examples.rs
new file mode 100644
index 00000000000..43cb8015de5
--- /dev/null
+++ b/tests/source/skip_macro_invocations/use_alias_examples.rs
@@ -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;
+);
diff --git a/tests/source/tuple.rs b/tests/source/tuple.rs
index 9a0f979fbca..5189a7454f3 100644
--- a/tests/source/tuple.rs
+++ b/tests/source/tuple.rs
@@ -1,4 +1,4 @@
-// Test tuple litterals
+// Test tuple literals
 
 fn foo() {
     let a = (a, a, a, a, a);
diff --git a/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs
index 78b3ce146f2..56064e4a4cc 100644
--- a/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs
+++ b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs
@@ -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() {}
diff --git a/tests/target/cfg_if/detect/arch/x86.rs b/tests/target/cfg_if/detect/arch/x86.rs
index 02d5eed1c29..47210cae2aa 100644
--- a/tests/target/cfg_if/detect/arch/x86.rs
+++ b/tests/target/cfg_if/detect/arch/x86.rs
@@ -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,
diff --git a/tests/target/configs/fn_args_layout/compressed.rs b/tests/target/configs/fn_params_layout/compressed.rs
index f189446e25d..ff32f0f1d58 100644
--- a/tests/target/configs/fn_args_layout/compressed.rs
+++ b/tests/target/configs/fn_params_layout/compressed.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/target/configs/fn_args_layout/tall.rs b/tests/target/configs/fn_params_layout/tall.rs
index 20f308973ac..25a86799af0 100644
--- a/tests/target/configs/fn_args_layout/tall.rs
+++ b/tests/target/configs/fn_params_layout/tall.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Tall
+// rustfmt-fn_params_layout: Tall
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/target/configs/fn_args_layout/vertical.rs b/tests/target/configs/fn_params_layout/vertical.rs
index 6c695a75df9..7a0e42415f3 100644
--- a/tests/target/configs/fn_args_layout/vertical.rs
+++ b/tests/target/configs/fn_params_layout/vertical.rs
@@ -1,4 +1,4 @@
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
 // Function arguments density
 
 trait Lorem {
diff --git a/tests/target/enum.rs b/tests/target/enum.rs
index 9a25126b44e..70fc8ab376c 100644
--- a/tests/target/enum.rs
+++ b/tests/target/enum.rs
@@ -43,7 +43,7 @@ enum StructLikeVariants {
         x: i32, // Test comment
         // Pre-comment
         #[Attr50]
-        y: SomeType, // Aanother Comment
+        y: SomeType, // Another Comment
     },
     SL {
         a: A,
diff --git a/tests/target/fn-custom-7.rs b/tests/target/fn-custom-7.rs
index 2c20ac5a752..f6a1a90c3fc 100644
--- a/tests/target/fn-custom-7.rs
+++ b/tests/target/fn-custom-7.rs
@@ -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.
diff --git a/tests/target/fn-custom.rs b/tests/target/fn-custom.rs
index 2eb2a973d24..506d9de3437 100644
--- a/tests/target/fn-custom.rs
+++ b/tests/target/fn-custom.rs
@@ -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.
diff --git a/tests/target/fn_args_layout-vertical.rs b/tests/target/fn_args_layout-vertical.rs
index da0ac981d87..bfeca15c967 100644
--- a/tests/target/fn_args_layout-vertical.rs
+++ b/tests/target/fn_args_layout-vertical.rs
@@ -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 {
diff --git a/tests/target/issue-2534/format_macro_matchers_false.rs b/tests/target/issue-2534/format_macro_matchers_false.rs
new file mode 100644
index 00000000000..2038ed7f1d0
--- /dev/null
+++ b/tests/target/issue-2534/format_macro_matchers_false.rs
@@ -0,0 +1,6 @@
+// rustfmt-format_macro_matchers: false
+
+macro_rules! foo {
+    ($a:ident : $b:ty) => {};
+    ($a:ident $b:ident $c:ident) => {};
+}
diff --git a/tests/target/issue-2534/format_macro_matchers_true.rs b/tests/target/issue-2534/format_macro_matchers_true.rs
new file mode 100644
index 00000000000..01d939add4d
--- /dev/null
+++ b/tests/target/issue-2534/format_macro_matchers_true.rs
@@ -0,0 +1,6 @@
+// rustfmt-format_macro_matchers: true
+
+macro_rules! foo {
+    ($a:ident : $b:ty) => {};
+    ($a:ident $b:ident $c:ident) => {};
+}
diff --git a/tests/target/issue-3987/format_macro_bodies_false.rs b/tests/target/issue-3987/format_macro_bodies_false.rs
new file mode 100644
index 00000000000..1352b762e45
--- /dev/null
+++ b/tests/target/issue-3987/format_macro_bodies_false.rs
@@ -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
+            ),
+        };
+    }};
+}
diff --git a/tests/target/issue-3987/format_macro_bodies_true.rs b/tests/target/issue-3987/format_macro_bodies_true.rs
new file mode 100644
index 00000000000..88d57159c85
--- /dev/null
+++ b/tests/target/issue-3987/format_macro_bodies_true.rs
@@ -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) };
+    }};
+}
diff --git a/tests/target/issue-4643.rs b/tests/target/issue-4643.rs
new file mode 100644
index 00000000000..ef99e4db382
--- /dev/null
+++ b/tests/target/issue-4643.rs
@@ -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;
+}
diff --git a/tests/target/issue-4791/issue_4928.rs b/tests/target/issue-4791/issue_4928.rs
index 588656b535f..29f6bda9063 100644
--- a/tests/target/issue-4791/issue_4928.rs
+++ b/tests/target/issue-4791/issue_4928.rs
@@ -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
diff --git a/tests/target/issue-5358.rs b/tests/target/issue-5358.rs
new file mode 100644
index 00000000000..d4bf4909ad7
--- /dev/null
+++ b/tests/target/issue-5358.rs
@@ -0,0 +1,4 @@
+// Test /* comment */ inside trait generics does not get duplicated.
+trait Test</* comment */ T> {}
+
+trait TestTwo</* comment */ T, /* comment */ V> {}
diff --git a/tests/target/issue_1306.rs b/tests/target/issue_1306.rs
new file mode 100644
index 00000000000..6bb514cdfe5
--- /dev/null
+++ b/tests/target/issue_1306.rs
@@ -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,
+    );
+}
diff --git a/tests/target/issue_3033.rs b/tests/target/issue_3033.rs
new file mode 100644
index 00000000000..e12249a6da6
--- /dev/null
+++ b/tests/target/issue_3033.rs
@@ -0,0 +1,2 @@
+use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding::
+    BluetoothRemoteGATTServerMethods;
diff --git a/tests/target/issue_3245.rs b/tests/target/issue_3245.rs
new file mode 100644
index 00000000000..8f442f1181a
--- /dev/null
+++ b/tests/target/issue_3245.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let x = 1;
+    let y = 3;
+}
diff --git a/tests/target/issue_3561.rs b/tests/target/issue_3561.rs
new file mode 100644
index 00000000000..846a14d86a5
--- /dev/null
+++ b/tests/target/issue_3561.rs
@@ -0,0 +1,7 @@
+fn main() {
+    7
+}
+
+fn main() {
+    7
+}
diff --git a/tests/target/issue_4350.rs b/tests/target/issue_4350.rs
new file mode 100644
index 00000000000..a94c5c32188
--- /dev/null
+++ b/tests/target/issue_4350.rs
@@ -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))
+    }};
+}
diff --git a/tests/target/skip_macro_invocations/all.rs b/tests/target/skip_macro_invocations/all.rs
new file mode 100644
index 00000000000..d0437ee10fd
--- /dev/null
+++ b/tests/target/skip_macro_invocations/all.rs
@@ -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;
+);
diff --git a/tests/target/skip_macro_invocations/all_and_name.rs b/tests/target/skip_macro_invocations/all_and_name.rs
new file mode 100644
index 00000000000..1f6722344fe
--- /dev/null
+++ b/tests/target/skip_macro_invocations/all_and_name.rs
@@ -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;
+);
diff --git a/tests/target/skip_macro_invocations/empty.rs b/tests/target/skip_macro_invocations/empty.rs
new file mode 100644
index 00000000000..4a398cc59c6
--- /dev/null
+++ b/tests/target/skip_macro_invocations/empty.rs
@@ -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;
+);
diff --git a/tests/target/skip_macro_invocations/name.rs b/tests/target/skip_macro_invocations/name.rs
new file mode 100644
index 00000000000..c4d577269c6
--- /dev/null
+++ b/tests/target/skip_macro_invocations/name.rs
@@ -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;
+);
diff --git a/tests/target/skip_macro_invocations/name_unknown.rs b/tests/target/skip_macro_invocations/name_unknown.rs
new file mode 100644
index 00000000000..7ab1440395c
--- /dev/null
+++ b/tests/target/skip_macro_invocations/name_unknown.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+    const _: u8 = 0;
+);
diff --git a/tests/target/skip_macro_invocations/names.rs b/tests/target/skip_macro_invocations/names.rs
new file mode 100644
index 00000000000..c6b41ff93d7
--- /dev/null
+++ b/tests/target/skip_macro_invocations/names.rs
@@ -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;
+);
diff --git a/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs
new file mode 100644
index 00000000000..6e372c72695
--- /dev/null
+++ b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+    const _: u8 = 0;
+);
diff --git a/tests/target/skip_macro_invocations/path_qualified_match.rs b/tests/target/skip_macro_invocations/path_qualified_match.rs
new file mode 100644
index 00000000000..9398918a9e1
--- /dev/null
+++ b/tests/target/skip_macro_invocations/path_qualified_match.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+        const _: u8 = 0;
+);
diff --git a/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs
new file mode 100644
index 00000000000..aa57a2a655c
--- /dev/null
+++ b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs
@@ -0,0 +1,6 @@
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+    const _: u8 = 0;
+);
diff --git a/tests/target/skip_macro_invocations/use_alias_examples.rs b/tests/target/skip_macro_invocations/use_alias_examples.rs
new file mode 100644
index 00000000000..799dd8c08af
--- /dev/null
+++ b/tests/target/skip_macro_invocations/use_alias_examples.rs
@@ -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;
+);
diff --git a/tests/target/tuple.rs b/tests/target/tuple.rs
index 68bb2f3bc28..24fcf8cfd7c 100644
--- a/tests/target/tuple.rs
+++ b/tests/target/tuple.rs
@@ -1,4 +1,4 @@
-// Test tuple litterals
+// Test tuple literals
 
 fn foo() {
     let a = (a, a, a, a, a);
diff --git a/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs
index d61d4d7c216..6ccecc7e0bb 100644
--- a/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs
+++ b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs
@@ -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() {}