summaryrefslogtreecommitdiffstats
path: root/src/tools/rustfmt/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rustfmt/src')
-rw-r--r--src/tools/rustfmt/src/attr.rs4
-rw-r--r--src/tools/rustfmt/src/bin/main.rs2
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/main.rs12
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/test/mod.rs4
-rw-r--r--src/tools/rustfmt/src/chains.rs124
-rw-r--r--src/tools/rustfmt/src/closures.rs3
-rw-r--r--src/tools/rustfmt/src/config/config_type.rs122
-rw-r--r--src/tools/rustfmt/src/config/macro_names.rs118
-rw-r--r--src/tools/rustfmt/src/config/mod.rs149
-rw-r--r--src/tools/rustfmt/src/expr.rs14
-rw-r--r--src/tools/rustfmt/src/imports.rs4
-rw-r--r--src/tools/rustfmt/src/items.rs10
-rw-r--r--src/tools/rustfmt/src/lib.rs1
-rw-r--r--src/tools/rustfmt/src/lists.rs16
-rw-r--r--src/tools/rustfmt/src/macros.rs26
-rw-r--r--src/tools/rustfmt/src/modules.rs13
-rw-r--r--src/tools/rustfmt/src/parse/parser.rs3
-rw-r--r--src/tools/rustfmt/src/parse/session.rs9
-rw-r--r--src/tools/rustfmt/src/skip.rs79
-rw-r--r--src/tools/rustfmt/src/test/configuration_snippet.rs7
-rw-r--r--src/tools/rustfmt/src/test/mod.rs6
-rw-r--r--src/tools/rustfmt/src/types.rs44
-rw-r--r--src/tools/rustfmt/src/utils.rs6
-rw-r--r--src/tools/rustfmt/src/visitor.rs13
24 files changed, 644 insertions, 145 deletions
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index c503eeeb9..5648e1254 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -336,7 +336,7 @@ impl Rewrite for ast::Attribute {
} else {
let should_skip = self
.ident()
- .map(|s| context.skip_context.skip_attribute(s.name.as_str()))
+ .map(|s| context.skip_context.attributes.skip(s.name.as_str()))
.unwrap_or(false);
let prefix = attr_prefix(self);
@@ -390,7 +390,7 @@ impl Rewrite for [ast::Attribute] {
// Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]`
// or `#![rustfmt::skip::attributes(derive)]`
- let skip_derives = context.skip_context.skip_attribute("derive");
+ let skip_derives = context.skip_context.attributes.skip("derive");
// This is not just a simple map because we need to handle doc comments
// (where we take as many doc comment attributes as possible) and possibly
diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 8e871e61f..be64559e8 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs
index 9031d29b4..2b714b68d 100644
--- a/src/tools/rustfmt/src/cargo-fmt/main.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/main.rs
@@ -198,12 +198,10 @@ fn convert_message_format_to_rustfmt_args(
Ok(())
}
"human" => Ok(()),
- _ => {
- return Err(format!(
- "invalid --message-format value: {}. Allowed values are: short|json|human",
- message_format
- ));
- }
+ _ => Err(format!(
+ "invalid --message-format value: {}. Allowed values are: short|json|human",
+ message_format
+ )),
}
}
@@ -215,7 +213,7 @@ fn print_usage_to_stderr(reason: &str) {
.expect("failed to write to stderr");
}
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verbosity {
Verbose,
Normal,
diff --git a/src/tools/rustfmt/src/cargo-fmt/test/mod.rs b/src/tools/rustfmt/src/cargo-fmt/test/mod.rs
index 56e52fbab..696326e4f 100644
--- a/src/tools/rustfmt/src/cargo-fmt/test/mod.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/test/mod.rs
@@ -70,9 +70,9 @@ fn mandatory_separator() {
.is_err()
);
assert!(
- !Opts::command()
+ Opts::command()
.try_get_matches_from(&["test", "--", "--emit"])
- .is_err()
+ .is_ok()
);
}
diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs
index a1a73cf4b..cbe523c6c 100644
--- a/src/tools/rustfmt/src/chains.rs
+++ b/src/tools/rustfmt/src/chains.rs
@@ -70,10 +70,66 @@ 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,
};
+use thin_vec::ThinVec;
+
+/// 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<'_>,
@@ -114,7 +170,7 @@ enum ChainItemKind {
MethodCall(
ast::PathSegment,
Vec<ast::GenericArg>,
- Vec<ptr::P<ast::Expr>>,
+ ThinVec<ptr::P<ast::Expr>>,
),
StructField(symbol::Ident),
TupleField(symbol::Ident, bool),
@@ -496,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> {
@@ -505,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,
}
}
@@ -517,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:
//
@@ -729,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(
@@ -808,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..];
@@ -827,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/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs
index 8fd0fcf8f..340113866 100644
--- a/src/tools/rustfmt/src/closures.rs
+++ b/src/tools/rustfmt/src/closures.rs
@@ -1,5 +1,6 @@
use rustc_ast::{ast, ptr};
use rustc_span::Span;
+use thin_vec::thin_vec;
use crate::attr::get_attrs_from_stmt;
use crate::config::lists::*;
@@ -150,7 +151,7 @@ fn rewrite_closure_with_block(
}
let block = ast::Block {
- stmts: vec![ast::Stmt {
+ stmts: thin_vec![ast::Stmt {
id: ast::NodeId::root(),
kind: ast::StmtKind::Expr(ptr::P(body.clone())),
span: body.span,
diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs
index c5e61658a..54ca7676d 100644
--- a/src/tools/rustfmt/src/config/config_type.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/config/macro_names.rs b/src/tools/rustfmt/src/config/macro_names.rs
new file mode 100644
index 000000000..26ad78d6d
--- /dev/null
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs
index f49c18d3a..14f27f3f8 100644
--- a/src/tools/rustfmt/src/config/mod.rs
+++ b/src/tools/rustfmt/src/config/mod.rs
@@ -13,15 +13,20 @@ pub use crate::config::file_lines::{FileLines, FileName, Range};
#[allow(unreachable_pub)]
pub use crate::config::lists::*;
#[allow(unreachable_pub)]
+pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
+#[allow(unreachable_pub)]
pub use crate::config::options::*;
#[macro_use]
pub(crate) mod config_type;
#[macro_use]
+#[allow(unreachable_pub)]
pub(crate) mod options;
pub(crate) mod file_lines;
+#[allow(unreachable_pub)]
pub(crate) mod lists;
+pub(crate) mod macro_names;
// This macro defines configuration options used in rustfmt. Each option
// is defined as follows:
@@ -67,6 +72,8 @@ create_config! {
format_macro_matchers: bool, false, false,
"Format the metavariable matching patterns in macros";
format_macro_bodies: bool, true, false, "Format the bodies of macros";
+ skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false,
+ "Skip formatting the bodies of macros invoked with the following names.";
hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
"Format hexadecimal integer literals";
@@ -119,7 +126,9 @@ create_config! {
force_multiline_blocks: bool, false, false,
"Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_layout: Density, Density::Tall, true,
- "Control the layout of arguments in a function";
+ "(deprecated: use fn_params_layout instead)";
+ fn_params_layout: Density, Density::Tall, true,
+ "Control the layout of parameters in function signatures.";
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
"Brace style for control flow constructs";
@@ -175,7 +184,7 @@ create_config! {
make_backup: bool, false, false, "Backup changed files";
print_misformatted_file_names: bool, false, true,
"Prints the names of mismatched files that were formatted. Prints the names of \
- files that would be formated when used with `--check` mode. ";
+ files that would be formatted when used with `--check` mode. ";
}
#[derive(Error, Debug)]
@@ -191,6 +200,7 @@ impl PartialConfig {
cloned.width_heuristics = None;
cloned.print_misformatted_file_names = None;
cloned.merge_imports = None;
+ cloned.fn_args_layout = None;
::toml::to_string(&cloned).map_err(ToTomlError)
}
@@ -403,11 +413,21 @@ mod test {
use super::*;
use std::str;
+ use crate::config::macro_names::MacroName;
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
#[allow(dead_code)]
mod mock {
use super::super::*;
+ use rustfmt_config_proc_macro::config_type;
+
+ #[config_type]
+ pub(crate) enum PartiallyUnstableOption {
+ V1,
+ V2,
+ #[unstable_variant]
+ V3,
+ }
create_config! {
// Options that are used by the generated functions
@@ -427,6 +447,12 @@ mod test {
"Merge imports";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
+ // fn_args_layout renamed to fn_params_layout
+ fn_args_layout: Density, Density::Tall, true,
+ "(deprecated: use fn_params_layout instead)";
+ fn_params_layout: Density, Density::Tall, true,
+ "Control the layout of parameters in a function signatures.";
+
// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true,
"Whether to use different formatting for items and \
@@ -451,6 +477,63 @@ mod test {
// Options that are used by the tests
stable_option: bool, false, true, "A stable option";
unstable_option: bool, false, false, "An unstable option";
+ partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true,
+ "A partially unstable option";
+ }
+
+ #[cfg(test)]
+ mod partially_unstable_option {
+ use super::{Config, PartialConfig, PartiallyUnstableOption};
+ use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
+ use std::path::Path;
+
+ /// From the config file, we can fill with a stable variant
+ #[test]
+ fn test_from_toml_stable_value() {
+ let toml = r#"
+ partially_unstable_option = "V2"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V2
+ );
+ }
+
+ /// From the config file, we cannot fill with an unstable variant (stable only)
+ #[stable_only_test]
+ #[test]
+ fn test_from_toml_unstable_value_on_stable() {
+ let toml = r#"
+ partially_unstable_option = "V3"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ // default value from config, i.e. fill failed
+ PartiallyUnstableOption::V1
+ );
+ }
+
+ /// From the config file, we can fill with an unstable variant (nightly only)
+ #[nightly_only_test]
+ #[test]
+ fn test_from_toml_unstable_value_on_nightly() {
+ let toml = r#"
+ partially_unstable_option = "V3"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V3
+ );
+ }
}
}
@@ -489,6 +572,11 @@ mod test {
assert_eq!(config.was_set().verbose(), false);
}
+ const PRINT_DOCS_STABLE_OPTION: &str = "stable_option <boolean> Default: false";
+ const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option <boolean> Default: false (unstable)";
+ const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str =
+ "partially_unstable_option [V1|V2|V3 (unstable)] Default: V1";
+
#[test]
fn test_print_docs_exclude_unstable() {
use self::mock::Config;
@@ -497,10 +585,9 @@ mod test {
Config::print_docs(&mut output, false);
let s = str::from_utf8(&output).unwrap();
-
- assert_eq!(s.contains("stable_option"), true);
- assert_eq!(s.contains("unstable_option"), false);
- assert_eq!(s.contains("(unstable)"), false);
+ assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false);
+ assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
@@ -511,9 +598,9 @@ mod test {
Config::print_docs(&mut output, true);
let s = str::from_utf8(&output).unwrap();
- assert_eq!(s.contains("stable_option"), true);
- assert_eq!(s.contains("unstable_option"), true);
- assert_eq!(s.contains("(unstable)"), true);
+ assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
@@ -541,6 +628,7 @@ normalize_doc_attributes = false
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
+skip_macro_invocations = []
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
@@ -567,7 +655,7 @@ enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
-fn_args_layout = "Tall"
+fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
@@ -921,4 +1009,45 @@ make_backup = false
assert_eq!(config.single_line_if_else_max_width(), 100);
}
}
+
+ #[cfg(test)]
+ mod partially_unstable_option {
+ use super::mock::{Config, PartiallyUnstableOption};
+ use super::*;
+
+ /// From the command line, we can override with a stable variant.
+ #[test]
+ fn test_override_stable_value() {
+ let mut config = Config::default();
+ config.override_value("partially_unstable_option", "V2");
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V2
+ );
+ }
+
+ /// From the command line, we can override with an unstable variant.
+ #[test]
+ fn test_override_unstable_value() {
+ let mut config = Config::default();
+ config.override_value("partially_unstable_option", "V3");
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V3
+ );
+ }
+ }
+
+ #[test]
+ fn test_override_skip_macro_invocations() {
+ let mut config = Config::default();
+ config.override_value("skip_macro_invocations", r#"["*", "println"]"#);
+ assert_eq!(
+ config.skip_macro_invocations(),
+ MacroSelectors(vec![
+ MacroSelector::All,
+ MacroSelector::Name(MacroName::new("println".to_owned()))
+ ])
+ );
+ }
}
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 868ff045a..3f0f217f8 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/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;
@@ -400,7 +400,10 @@ pub(crate) fn format_expr(
}
}
ast::ExprKind::Underscore => Some("_".to_owned()),
- ast::ExprKind::IncludedBytes(..) => unreachable!(),
+ ast::ExprKind::FormatArgs(..) | ast::ExprKind::IncludedBytes(..) => {
+ // These do not occur in the AST because macros aren't expanded.
+ unreachable!()
+ }
ast::ExprKind::Err => None,
};
@@ -2050,8 +2053,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/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index d9dc8d004..339e5cef5 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index a2a73f0a5..25e8a0248 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/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(
@@ -1241,7 +1245,7 @@ fn format_unit_struct(
) -> Option<String> {
let header_str = format_header(context, p.prefix, p.ident, p.vis, offset);
let generics_str = if let Some(generics) = p.generics {
- let hi = context.snippet_provider.span_before(p.span, ";");
+ let hi = context.snippet_provider.span_before_last(p.span, ";");
format_generics(
context,
generics,
@@ -2602,7 +2606,7 @@ fn rewrite_params(
&param_items,
context
.config
- .fn_args_layout()
+ .fn_params_layout()
.to_list_tactic(param_items.len()),
Separator::Comma,
one_line_budget,
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index 0c27bcacf..b27405efd 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -23,6 +23,7 @@ extern crate rustc_expand;
extern crate rustc_parse;
extern crate rustc_session;
extern crate rustc_span;
+extern crate thin_vec;
// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta
// files.
diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs
index e87850507..a878e6cf9 100644
--- a/src/tools/rustfmt/src/lists.rs
+++ b/src/tools/rustfmt/src/lists.rs
@@ -297,9 +297,9 @@ where
} else {
inner_item.as_ref()
};
- let mut item_last_line_width = item_last_line.len() + item_sep_len;
+ let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len;
if item_last_line.starts_with(&**indent_str) {
- item_last_line_width -= indent_str.len();
+ item_last_line_width -= unicode_str_width(indent_str);
}
if !item.is_substantial() {
@@ -449,7 +449,7 @@ where
} else if starts_with_newline(comment) {
false
} else {
- comment.trim().contains('\n') || comment.trim().len() > width
+ comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width
};
rewrite_comment(
@@ -465,7 +465,7 @@ where
if !starts_with_newline(comment) {
if formatting.align_comments {
let mut comment_alignment =
- post_comment_alignment(item_max_width, inner_item.len());
+ post_comment_alignment(item_max_width, unicode_str_width(inner_item));
if first_line_width(&formatted_comment)
+ last_line_width(&result)
+ comment_alignment
@@ -475,7 +475,7 @@ where
item_max_width = None;
formatted_comment = rewrite_post_comment(&mut item_max_width)?;
comment_alignment =
- post_comment_alignment(item_max_width, inner_item.len());
+ post_comment_alignment(item_max_width, unicode_str_width(inner_item));
}
for _ in 0..=comment_alignment {
result.push(' ');
@@ -533,7 +533,7 @@ where
let mut first = true;
for item in items.clone().into_iter().skip(i) {
let item = item.as_ref();
- let inner_item_width = item.inner_as_ref().len();
+ let inner_item_width = unicode_str_width(item.inner_as_ref());
if !first
&& (item.is_different_group()
|| item.post_comment.is_none()
@@ -552,8 +552,8 @@ where
max_width
}
-fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize) -> usize {
- item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
+fn post_comment_alignment(item_max_width: Option<usize>, inner_item_width: usize) -> usize {
+ item_max_width.unwrap_or(0).saturating_sub(inner_item_width)
}
pub(crate) struct ListItems<'a, I, F1, F2, F3>
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index df9493880..7978d8cba 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -13,7 +13,7 @@ use std::collections::HashMap;
use std::panic::{catch_unwind, AssertUnwindSafe};
use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::{ast, ptr};
use rustc_ast_pretty::pprust;
use rustc_span::{
@@ -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 {
@@ -735,7 +736,7 @@ impl MacroArgParser {
self.buf.clear();
}
- fn add_meta_variable(&mut self, iter: &mut Cursor) -> Option<()> {
+ fn add_meta_variable(&mut self, iter: &mut TokenTreeCursor) -> Option<()> {
match iter.next() {
Some(TokenTree::Token(
Token {
@@ -767,7 +768,7 @@ impl MacroArgParser {
&mut self,
inner: Vec<ParsedMacroArg>,
delim: Delimiter,
- iter: &mut Cursor,
+ iter: &mut TokenTreeCursor,
) -> Option<()> {
let mut buffer = String::new();
let mut first = true;
@@ -1120,7 +1121,7 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D
// Currently we do not attempt to parse any further than that.
#[derive(new)]
struct MacroParser {
- toks: Cursor,
+ toks: TokenTreeCursor,
}
impl MacroParser {
@@ -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/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs
index 7a0d1736c..af9a154a6 100644
--- a/src/tools/rustfmt/src/modules.rs
+++ b/src/tools/rustfmt/src/modules.rs
@@ -6,6 +6,7 @@ use rustc_ast::ast;
use rustc_ast::visit::Visitor;
use rustc_span::symbol::{self, sym, Symbol};
use rustc_span::Span;
+use thin_vec::ThinVec;
use thiserror::Error;
use crate::attr::MetaVisitor;
@@ -25,7 +26,7 @@ type FileModMap<'ast> = BTreeMap<FileName, Module<'ast>>;
#[derive(Debug, Clone)]
pub(crate) struct Module<'a> {
ast_mod_kind: Option<Cow<'a, ast::ModKind>>,
- pub(crate) items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>,
+ pub(crate) items: Cow<'a, ThinVec<rustc_ast::ptr::P<ast::Item>>>,
inner_attr: ast::AttrVec,
pub(crate) span: Span,
}
@@ -34,7 +35,7 @@ impl<'a> Module<'a> {
pub(crate) fn new(
mod_span: Span,
ast_mod_kind: Option<Cow<'a, ast::ModKind>>,
- mod_items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>,
+ mod_items: Cow<'a, ThinVec<rustc_ast::ptr::P<ast::Item>>>,
mod_attrs: Cow<'a, ast::AttrVec>,
) -> Self {
let inner_attr = mod_attrs
@@ -157,7 +158,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
Module::new(
module_item.item.span,
Some(Cow::Owned(sub_mod_kind.clone())),
- Cow::Owned(vec![]),
+ Cow::Owned(ThinVec::new()),
Cow::Owned(ast::AttrVec::new()),
),
)?;
@@ -169,7 +170,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
/// Visit modules defined inside macro calls.
fn visit_mod_outside_ast(
&mut self,
- items: Vec<rustc_ast::ptr::P<ast::Item>>,
+ items: ThinVec<rustc_ast::ptr::P<ast::Item>>,
) -> Result<(), ModuleResolutionError> {
for item in items {
if is_cfg_if(&item) {
@@ -184,7 +185,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
Module::new(
span,
Some(Cow::Owned(sub_mod_kind.clone())),
- Cow::Owned(vec![]),
+ Cow::Owned(ThinVec::new()),
Cow::Owned(ast::AttrVec::new()),
),
)?;
@@ -210,7 +211,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
Module::new(
span,
Some(Cow::Borrowed(sub_mod_kind)),
- Cow::Owned(vec![]),
+ Cow::Owned(ThinVec::new()),
Cow::Borrowed(&item.attrs),
),
)?;
diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs
index e0bd06551..7ab042506 100644
--- a/src/tools/rustfmt/src/parse/parser.rs
+++ b/src/tools/rustfmt/src/parse/parser.rs
@@ -6,6 +6,7 @@ use rustc_ast::{ast, ptr};
use rustc_errors::Diagnostic;
use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
use rustc_span::{sym, Span};
+use thin_vec::ThinVec;
use crate::attr::first_attr_value_str_by_name;
use crate::parse::session::ParseSess;
@@ -109,7 +110,7 @@ impl<'a> Parser<'a> {
sess: &'a ParseSess,
path: &Path,
span: Span,
- ) -> Result<(ast::AttrVec, Vec<ptr::P<ast::Item>>, Span), ParserError> {
+ ) -> Result<(ast::AttrVec, ThinVec<ptr::P<ast::Item>>, Span), ParserError> {
let result = catch_unwind(AssertUnwindSafe(|| {
let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
match parser.parse_mod(&TokenKind::Eof) {
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 6bfec79cd..a64963db6 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use rustc_data_structures::sync::{Lrc, Send};
use rustc_errors::emitter::{Emitter, EmitterWriter};
use rustc_errors::translation::Translate;
-use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
+use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
source_map::{FilePathMapping, SourceMap},
@@ -123,8 +123,10 @@ fn default_handler(
let emitter = if hide_parse_errors {
silent_emitter()
} else {
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+ rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
+ false,
+ );
Box::new(EmitterWriter::stderr(
color_cfg,
Some(source_map.clone()),
@@ -135,6 +137,7 @@ fn default_handler(
None,
false,
false,
+ TerminalUrl::No,
))
};
Handler::with_emitter(
diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs
index 032922d42..68f85b2ad 100644
--- a/src/tools/rustfmt/src/skip.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs
index c8fda7c85..c70b3c5fa 100644
--- a/src/tools/rustfmt/src/test/configuration_snippet.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs
index 6b5bc2b30..cfad4a8ed 100644
--- a/src/tools/rustfmt/src/test/mod.rs
+++ b/src/tools/rustfmt/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/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index c1991e8d2..01e2fb6e6 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -941,6 +941,28 @@ fn join_bounds_inner(
ast::GenericBound::Trait(..) => last_line_extendable(s),
};
+ // Whether a GenericBound item is a PathSegment segment that includes internal array
+ // that contains more than one item
+ let is_item_with_multi_items_array = |item: &ast::GenericBound| match item {
+ ast::GenericBound::Trait(ref poly_trait_ref, ..) => {
+ let segments = &poly_trait_ref.trait_ref.path.segments;
+ if segments.len() > 1 {
+ true
+ } else {
+ if let Some(args_in) = &segments[0].args {
+ matches!(
+ args_in.deref(),
+ ast::GenericArgs::AngleBracketed(bracket_args)
+ if bracket_args.args.len() > 1
+ )
+ } else {
+ false
+ }
+ }
+ }
+ _ => false,
+ };
+
let result = items.iter().enumerate().try_fold(
(String::new(), None, false),
|(strs, prev_trailing_span, prev_extendable), (i, item)| {
@@ -1035,10 +1057,24 @@ fn join_bounds_inner(
},
)?;
- if !force_newline
- && items.len() > 1
- && (result.0.contains('\n') || result.0.len() > shape.width)
- {
+ // Whether to retry with a forced newline:
+ // Only if result is not already multiline and did not exceed line width,
+ // and either there is more than one item;
+ // or the single item is of type `Trait`,
+ // and any of the internal arrays contains more than one item;
+ let retry_with_force_newline = match context.config.version() {
+ Version::One => {
+ !force_newline
+ && items.len() > 1
+ && (result.0.contains('\n') || result.0.len() > shape.width)
+ }
+ Version::Two if force_newline => false,
+ Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
+ Version::Two if items.len() > 1 => true,
+ Version::Two => is_item_with_multi_items_array(&items[0]),
+ };
+
+ if retry_with_force_newline {
join_bounds_inner(context, shape, items, need_indent, true)
} else {
Some(result.0)
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 3e884419f..1e89f3ae7 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/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 {
@@ -462,6 +463,7 @@ pub(crate) fn first_line_ends_with(s: &str, c: char) -> bool {
pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr: &str) -> bool {
match expr.kind {
ast::ExprKind::MacCall(..)
+ | ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Call(..)
| ast::ExprKind::MethodCall(..)
| ast::ExprKind::Array(..)
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index 9c3cc7820..f4d84d138 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/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,
}
}