Merge remote-tracking branch 'upstream/master' into subtree-push-nightly-2024-09-10

This commit is contained in:
Yacin Tmimi 2024-09-10 01:15:02 -04:00
commit 348a499e8b
187 changed files with 4410 additions and 1466 deletions

View file

@ -1,21 +1,21 @@
//! Format attributes and meta items.
use rustc_ast::ast;
use rustc_ast::HasAttrs;
use rustc_span::{symbol::sym, Span};
use rustc_ast::ast;
use rustc_span::{Span, symbol::sym};
use tracing::debug;
use self::doc_comment::DocCommentFormatter;
use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle};
use crate::config::lists::*;
use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment};
use crate::config::IndentStyle;
use crate::config::lists::*;
use crate::expr::rewrite_literal;
use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
use crate::overflow;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::types::{rewrite_path, PathContext};
use crate::types::{PathContext, rewrite_path};
use crate::utils::{count_newlines, mk_sp};
mod doc_comment;
@ -100,7 +100,7 @@ fn format_derive(
",",
|span| span.lo(),
|span| span.hi(),
|span| Some(context.snippet(*span).to_owned()),
|span| Ok(context.snippet(*span).to_owned()),
// We update derive attribute spans to start after the opening '('
// This helps us focus parsing to just what's inside #[derive(...)]
context.snippet_provider.span_after(attr.span, "("),
@ -148,7 +148,7 @@ fn format_derive(
.tactic(tactic)
.trailing_separator(trailing_separator)
.ends_with_newline(false);
let item_str = write_list(&all_items, &fmt)?;
let item_str = write_list(&all_items, &fmt).ok()?;
debug!("item_str: '{}'", item_str);
@ -218,9 +218,9 @@ fn rewrite_initial_doc_comments(
context: &RewriteContext<'_>,
attrs: &[ast::Attribute],
shape: Shape,
) -> Option<(usize, Option<String>)> {
) -> Result<(usize, Option<String>), RewriteError> {
if attrs.is_empty() {
return Some((0, None));
return Ok((0, None));
}
// Rewrite doc comments
let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment());
@ -230,7 +230,7 @@ fn rewrite_initial_doc_comments(
.map(|a| context.snippet(a.span))
.collect::<Vec<_>>()
.join("\n");
return Some((
return Ok((
sugared_docs.len(),
Some(rewrite_doc_comment(
&snippet,
@ -240,13 +240,19 @@ fn rewrite_initial_doc_comments(
));
}
Some((0, None))
Ok((0, None))
}
impl Rewrite for ast::NestedMetaItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self {
ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
ast::NestedMetaItem::MetaItem(ref meta_item) => {
meta_item.rewrite_result(context, shape)
}
ast::NestedMetaItem::Lit(ref l) => {
rewrite_literal(context, l.as_token_lit(), l.span, shape)
}
@ -275,7 +281,11 @@ fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
impl Rewrite for ast::MetaItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(match self.kind {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
Ok(match self.kind {
ast::MetaItemKind::Word => {
rewrite_path(context, PathContext::Type, &None, &self.path, shape)?
}
@ -287,7 +297,7 @@ impl Rewrite for ast::MetaItem {
&path,
list.iter(),
// 1 = "]"
shape.sub_width(1)?,
shape.sub_width(1).max_width_error(shape.width, self.span)?,
self.span,
context.config.attr_fn_like_width(),
Some(if has_trailing_comma {
@ -300,7 +310,9 @@ impl Rewrite for ast::MetaItem {
ast::MetaItemKind::NameValue(ref lit) => {
let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?;
// 3 = ` = `
let lit_shape = shape.shrink_left(path.len() + 3)?;
let lit_shape = shape
.shrink_left(path.len() + 3)
.max_width_error(shape.width, self.span)?;
// `rewrite_literal` returns `None` when `lit` exceeds max
// width. Since a literal is basically unformattable unless it
// is a string literal (and only if `format_strings` is set),
@ -308,7 +320,7 @@ impl Rewrite for ast::MetaItem {
// is longer than the max width and continue on formatting.
// See #2479 for example.
let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape)
.unwrap_or_else(|| context.snippet(lit.span).to_owned());
.unwrap_or_else(|_| context.snippet(lit.span).to_owned());
format!("{path} = {value}")
}
})
@ -317,6 +329,10 @@ impl Rewrite for ast::MetaItem {
impl Rewrite for ast::Attribute {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let snippet = context.snippet(self.span);
if self.is_doc_comment() {
rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
@ -328,7 +344,7 @@ impl Rewrite for ast::Attribute {
let prefix = attr_prefix(self);
if should_skip || contains_comment(snippet) {
return Some(snippet.to_owned());
return Ok(snippet.to_owned());
}
if let Some(ref meta) = self.meta() {
@ -353,9 +369,11 @@ impl Rewrite for ast::Attribute {
}
// 1 = `[`
let shape = shape.offset_left(prefix.len() + 1)?;
Some(meta.rewrite(context, shape).map_or_else(
|| snippet.to_owned(),
let shape = shape
.offset_left(prefix.len() + 1)
.max_width_error(shape.width, self.span)?;
Ok(meta.rewrite_result(context, shape).map_or_else(
|_| snippet.to_owned(),
|rw| match &self.kind {
ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety {
// For #![feature(unsafe_attributes)]
@ -367,7 +385,7 @@ impl Rewrite for ast::Attribute {
},
))
} else {
Some(snippet.to_owned())
Ok(snippet.to_owned())
}
}
}
@ -375,8 +393,12 @@ impl Rewrite for ast::Attribute {
impl Rewrite for [ast::Attribute] {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if self.is_empty() {
return Some(String::new());
return Ok(String::new());
}
// The current remaining attributes.
@ -392,7 +414,7 @@ impl Rewrite for [ast::Attribute] {
// merging derives into a single attribute.
loop {
if attrs.is_empty() {
return Some(result);
return Ok(result);
}
// Handle doc comments.
@ -431,7 +453,7 @@ impl Rewrite for [ast::Attribute] {
// Handle derives if we will merge them.
if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) {
let derives = take_while_with_pred(context, attrs, is_derive);
let derive_str = format_derive(derives, shape, context)?;
let derive_str = format_derive(derives, shape, context).unknown_error()?;
result.push_str(&derive_str);
let missing_span = attrs
@ -464,7 +486,7 @@ impl Rewrite for [ast::Attribute] {
// If we get here, then we have a regular attribute, just handle one
// at a time.
let formatted_attr = attrs[0].rewrite(context, shape)?;
let formatted_attr = attrs[0].rewrite_result(context, shape)?;
result.push_str(&formatted_attr);
let missing_span = attrs

View file

@ -1,6 +1,6 @@
#![feature(rustc_private)]
use anyhow::{format_err, Result};
use anyhow::{Result, format_err};
use io::Error as IoError;
use thiserror::Error;
@ -11,15 +11,15 @@ use tracing_subscriber::EnvFilter;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, stdout, Read, Write};
use std::io::{self, Read, Write, stdout};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use getopts::{Matches, Options};
use crate::rustfmt::{
load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
FormatReportFormatterBuilder, Input, Session, Verbosity,
CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config,
};
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug";
@ -129,7 +129,12 @@ fn make_opts() -> Options {
found reverts to the input file path",
"[Path for the configuration file]",
);
opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]");
opts.optopt(
"",
"edition",
"Rust edition to use",
"[2015|2018|2021|2024]",
);
opts.optopt(
"",
"color",
@ -181,6 +186,12 @@ fn make_opts() -> Options {
"skip-children",
"Don't reformat child modules (unstable).",
);
opts.optopt(
"",
"style-edition",
"The edition of the Style Guide (unstable).",
"[2015|2018|2021|2024]",
);
}
opts.optflag("v", "verbose", "Print verbose output");
@ -263,24 +274,35 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
if options.check {
config.set().emit_mode(EmitMode::Diff);
config.set_cli().emit_mode(EmitMode::Diff);
} else {
match options.emit_mode {
// Emit modes which work with standard input
// None means default, which is Stdout.
None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {}
None => {
config
.set()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
}
Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {
config
.set_cli()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
}
Some(emit_mode) => {
return Err(OperationError::StdinBadEmit(emit_mode).into());
}
}
config
.set()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
}
config.set().verbose(Verbosity::Quiet);
// parse file_lines
config.set().file_lines(options.file_lines);
if options.file_lines.is_all() {
config.set().file_lines(options.file_lines);
} else {
config.set_cli().file_lines(options.file_lines);
}
for f in config.file_lines().files() {
match *f {
FileName::Stdin => {}
@ -428,27 +450,27 @@ are included as out of line modules from `src/lib.rs`."
}
fn print_version() {
let version_info = format!(
"{}-{}",
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
);
let version_number = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
let commit_info = include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"));
println!("rustfmt {version_info}");
if commit_info.is_empty() {
println!("rustfmt {version_number}");
} else {
println!("rustfmt {version_number}-{commit_info}");
}
}
fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
if matches.opt_present("h") {
let topic = matches.opt_str("h");
if topic.is_none() {
let Some(topic) = matches.opt_str("h") else {
return Ok(Operation::Help(HelpOp::None));
} else if topic == Some("config".to_owned()) {
return Ok(Operation::Help(HelpOp::Config));
} else if topic == Some("file-lines".to_owned()) && is_nightly() {
return Ok(Operation::Help(HelpOp::FileLines));
} else {
return Err(OperationError::UnknownHelpTopic(topic.unwrap()));
}
};
return match topic.as_str() {
"config" => Ok(Operation::Help(HelpOp::Config)),
"file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)),
_ => Err(OperationError::UnknownHelpTopic(topic)),
};
}
let mut free_matches = matches.free.iter();
@ -514,6 +536,7 @@ struct GetOptsOptions {
backup: bool,
check: bool,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
color: Option<Color>,
file_lines: FileLines, // Default is all lines in all files.
unstable_features: bool,
@ -545,6 +568,10 @@ impl GetOptsOptions {
if let Some(ref file_lines) = matches.opt_str("file-lines") {
options.file_lines = file_lines.parse()?;
}
if let Some(ref edition_str) = matches.opt_str("style-edition") {
options.style_edition =
Some(style_edition_from_style_edition_str(edition_str)?);
}
} else {
let mut unstable_options = vec![];
if matches.opt_present("skip-children") {
@ -556,6 +583,9 @@ impl GetOptsOptions {
if matches.opt_present("file-lines") {
unstable_options.push("`--file-lines`");
}
if matches.opt_present("style-edition") {
unstable_options.push("`--style-edition`");
}
if !unstable_options.is_empty() {
let s = if unstable_options.len() == 1 { "" } else { "s" };
return Err(format_err!(
@ -650,36 +680,49 @@ impl GetOptsOptions {
impl CliOptions for GetOptsOptions {
fn apply_to(self, config: &mut Config) {
if self.verbose {
config.set().verbose(Verbosity::Verbose);
config.set_cli().verbose(Verbosity::Verbose);
} else if self.quiet {
config.set().verbose(Verbosity::Quiet);
config.set_cli().verbose(Verbosity::Quiet);
} else {
config.set().verbose(Verbosity::Normal);
}
config.set().file_lines(self.file_lines);
config.set().unstable_features(self.unstable_features);
if self.file_lines.is_all() {
config.set().file_lines(self.file_lines);
} else {
config.set_cli().file_lines(self.file_lines);
}
if self.unstable_features {
config.set_cli().unstable_features(self.unstable_features);
} else {
config.set().unstable_features(self.unstable_features);
}
if let Some(skip_children) = self.skip_children {
config.set().skip_children(skip_children);
config.set_cli().skip_children(skip_children);
}
if let Some(error_on_unformatted) = self.error_on_unformatted {
config.set().error_on_unformatted(error_on_unformatted);
config.set_cli().error_on_unformatted(error_on_unformatted);
}
if let Some(edition) = self.edition {
config.set().edition(edition);
config.set_cli().edition(edition);
}
if let Some(edition) = self.style_edition {
config.set_cli().style_edition(edition);
}
if self.check {
config.set().emit_mode(EmitMode::Diff);
config.set_cli().emit_mode(EmitMode::Diff);
} else if let Some(emit_mode) = self.emit_mode {
config.set().emit_mode(emit_mode);
config.set_cli().emit_mode(emit_mode);
}
if self.backup {
config.set().make_backup(true);
config.set_cli().make_backup(true);
}
if let Some(color) = self.color {
config.set().color(color);
config.set_cli().color(color);
}
if self.print_misformatted_file_names {
config.set().print_misformatted_file_names(true);
config.set_cli().print_misformatted_file_names(true);
}
for (key, val) in self.inline_config {
@ -690,6 +733,25 @@ impl CliOptions for GetOptsOptions {
fn config_path(&self) -> Option<&Path> {
self.config_path.as_deref()
}
fn edition(&self) -> Option<Edition> {
self.inline_config
.get("edition")
.map_or(self.edition, |e| Edition::from_str(e).ok())
}
fn style_edition(&self) -> Option<StyleEdition> {
self.inline_config
.get("style_edition")
.map_or(self.style_edition, |se| StyleEdition::from_str(se).ok())
}
fn version(&self) -> Option<Version> {
self.inline_config
.get("version")
.map(|version| Version::from_str(version).ok())
.flatten()
}
}
fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
}
}
fn style_edition_from_style_edition_str(edition_str: &str) -> Result<StyleEdition> {
match edition_str {
"2015" => Ok(StyleEdition::Edition2015),
"2018" => Ok(StyleEdition::Edition2018),
"2021" => Ok(StyleEdition::Edition2021),
"2024" => Ok(StyleEdition::Edition2024),
_ => Err(format_err!("Invalid value for `--style-edition`")),
}
}
fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
match emit_str {
"files" => Ok(EmitMode::Files),
@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
_ => Err(format_err!("Invalid value for `--emit`")),
}
}
#[cfg(test)]
#[allow(dead_code)]
mod test {
use super::*;
use rustfmt_config_proc_macro::nightly_only_test;
fn get_config<O: CliOptions>(path: Option<&Path>, options: Option<O>) -> Config {
load_config(path, options).unwrap().0
}
#[nightly_only_test]
#[test]
fn flag_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn edition_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.edition = Some(Edition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn version_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn version_config_file_sets_style_edition_override_correctly() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-version"));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_edition() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2021);
options.edition = Some(Edition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_version() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2018);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_edition_version() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2021);
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_inline_has_correct_precedence_over_edition_version() {
let mut options = GetOptsOptions::default();
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([
("version".to_owned(), "One".to_owned()),
("style_edition".to_owned(), "2024".to_owned()),
]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_flag_version_inline() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "One".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_config_and_version_inline() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/style-edition-and-edition",
));
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
assert_eq!(config.edition(), Edition::Edition2024);
}
#[nightly_only_test]
#[test]
fn version_config_trumps_edition_config_and_flag() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/version-edition"));
options.edition = Some(Edition::Edition2018);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_version_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/version-style-edition",
));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_version_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/version-style-edition-and-edition",
));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn correct_defaults_for_style_edition_loaded() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_defaults_overridden_from_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/overrides"));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), false);
}
#[nightly_only_test]
#[test]
fn style_edition_defaults_overridden_from_cli() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
options.inline_config =
HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), false);
}
}

View file

@ -59,15 +59,15 @@ use std::borrow::Cow;
use std::cmp::min;
use rustc_ast::{ast, ptr};
use rustc_span::{symbol, BytePos, Span};
use rustc_span::{BytePos, Span, symbol};
use tracing::debug;
use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar};
use crate::config::{IndentStyle, Version};
use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment};
use crate::config::{IndentStyle, StyleEdition};
use crate::expr::rewrite_call;
use crate::lists::extract_pre_comment;
use crate::macros::convert_try_mac;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::utils::{
@ -80,6 +80,9 @@ 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> {
// TODO(ding-young): Currently returning None when the given span is out of the range
// covered by the snippet provider. If this is a common cause for internal
// rewrite failure, add a new enum variant and return RewriteError instead of None
context.snippet_provider.span_to_snippet(span).map(|s| {
s.lines()
.map(|l| l.trim_end())
@ -93,12 +96,16 @@ fn format_chain_item(
context: &RewriteContext<'_>,
rewrite_shape: Shape,
allow_overflow: bool,
) -> Option<String> {
) -> RewriteResult {
if allow_overflow {
item.rewrite(context, rewrite_shape)
.or_else(|| format_overflow_style(item.span, context))
// TODO(ding-young): Consider calling format_overflow_style()
// only when item.rewrite_result() returns RewriteError::ExceedsMaxWidth.
// It may be inappropriate to call format_overflow_style on other RewriteError
// since the current approach retries formatting if allow_overflow is true
item.rewrite_result(context, rewrite_shape)
.or_else(|_| format_overflow_style(item.span, context).unknown_error())
} else {
item.rewrite(context, rewrite_shape)
item.rewrite_result(context, rewrite_shape)
}
}
@ -135,17 +142,17 @@ pub(crate) fn rewrite_chain(
expr: &ast::Expr,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let chain = Chain::from_ast(expr, context);
debug!("rewrite_chain {:?} {:?}", chain, shape);
// If this is just an expression with some `?`s, then format it trivially and
// return early.
if chain.children.is_empty() {
return chain.parent.rewrite(context, shape);
return chain.parent.rewrite_result(context, shape);
}
chain.rewrite(context, shape)
chain.rewrite_result(context, shape)
}
#[derive(Debug)]
@ -203,7 +210,7 @@ impl ChainItemKind {
fn is_tup_field_access(expr: &ast::Expr) -> bool {
match expr.kind {
ast::ExprKind::Field(_, ref field) => {
field.name.to_string().chars().all(|c| c.is_digit(10))
field.name.as_str().chars().all(|c| c.is_digit(10))
}
_ => false,
}
@ -269,7 +276,13 @@ impl ChainItemKind {
impl Rewrite for ChainItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let shape = shape.sub_width(self.tries)?;
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let shape = shape
.sub_width(self.tries)
.max_width_error(shape.width, self.span)?;
let rewrite = match self.kind {
ChainItemKind::Parent {
ref expr,
@ -278,14 +291,14 @@ impl Rewrite for ChainItem {
ChainItemKind::Parent {
ref expr,
parens: false,
} => expr.rewrite(context, shape)?,
} => expr.rewrite_result(context, shape)?,
ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
}
ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)),
ChainItemKind::TupleField(ident, nested) => format!(
"{}.{}",
if nested && context.config.version() == Version::One {
if nested && context.config.style_edition() <= StyleEdition::Edition2021 {
" "
} else {
""
@ -297,7 +310,7 @@ impl Rewrite for ChainItem {
rewrite_comment(comment, false, shape, context.config)?
}
};
Some(format!("{rewrite}{}", "?".repeat(self.tries)))
Ok(format!("{rewrite}{}", "?".repeat(self.tries)))
}
}
@ -327,14 +340,14 @@ impl ChainItem {
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let type_str = if types.is_empty() {
String::new()
} else {
let type_list = types
.iter()
.map(|ty| ty.rewrite(context, shape))
.collect::<Option<Vec<_>>>()?;
.map(|ty| ty.rewrite_result(context, shape))
.collect::<Result<Vec<_>, RewriteError>>()?;
format!("::<{}>", type_list.join(", "))
};
@ -519,6 +532,10 @@ impl Chain {
impl Rewrite for Chain {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
debug!("rewrite chain {:?} {:?}", self, shape);
let mut formatter = match context.config.indent_style() {
@ -532,17 +549,25 @@ impl Rewrite for Chain {
formatter.format_root(&self.parent, context, shape)?;
if let Some(result) = formatter.pure_root() {
return wrap_str(result, context.config.max_width(), shape);
return wrap_str(result, context.config.max_width(), shape)
.max_width_error(shape.width, self.parent.span);
}
let first = self.children.first().unwrap_or(&self.parent);
let last = self.children.last().unwrap_or(&self.parent);
let children_span = mk_sp(first.span.lo(), last.span.hi());
let full_span = self.parent.span.with_hi(children_span.hi());
// Decide how to layout the rest of the chain.
let child_shape = formatter.child_shape(context, shape)?;
let child_shape = formatter
.child_shape(context, shape)
.max_width_error(shape.width, children_span)?;
formatter.format_children(context, child_shape)?;
formatter.format_last_child(context, shape, child_shape)?;
let result = formatter.join_rewrites(context, child_shape)?;
wrap_str(result, context.config.max_width(), shape)
wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
}
}
@ -564,16 +589,20 @@ trait ChainFormatter {
parent: &ChainItem,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<()>;
) -> Result<(), RewriteError>;
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>;
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>;
fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError>;
fn format_last_child(
&mut self,
context: &RewriteContext<'_>,
shape: Shape,
child_shape: Shape,
) -> Option<()>;
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>;
) -> Result<(), RewriteError>;
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult;
// Returns `Some` if the chain is only a root, None otherwise.
fn pure_root(&mut self) -> Option<String>;
}
@ -616,12 +645,16 @@ impl<'a> ChainFormatterShared<'a> {
}
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
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(())
Ok(())
}
// Rewrite the last child. The last child of a chain requires special treatment. We need to
@ -662,8 +695,8 @@ impl<'a> ChainFormatterShared<'a> {
context: &RewriteContext<'_>,
shape: Shape,
child_shape: Shape,
) -> Option<()> {
let last = self.children.last()?;
) -> Result<(), RewriteError> {
let last = self.children.last().unknown_error()?;
let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
let prev_last_line_width = last_line_width(&self.rewrites[0]);
@ -687,11 +720,17 @@ impl<'a> ChainFormatterShared<'a> {
&& self.rewrites.iter().all(|s| !s.contains('\n'))
&& one_line_budget > 0;
let last_shape = if all_in_one_line {
shape.sub_width(last.tries)?
shape
.sub_width(last.tries)
.max_width_error(shape.width, last.span)?
} else if extendable {
child_shape.sub_width(last.tries)?
child_shape
.sub_width(last.tries)
.max_width_error(child_shape.width, last.span)?
} else {
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)
.max_width_error(child_shape.width, last.span)?
};
let mut last_subexpr_str = None;
@ -707,7 +746,7 @@ impl<'a> ChainFormatterShared<'a> {
};
if let Some(one_line_shape) = one_line_shape {
if let Some(rw) = last.rewrite(context, one_line_shape) {
if let Ok(rw) = last.rewrite_result(context, one_line_shape) {
// We allow overflowing here only if both of the following conditions match:
// 1. The entire chain fits in a single line except the last child.
// 2. `last_child_str.lines().count() >= 5`.
@ -722,17 +761,18 @@ impl<'a> ChainFormatterShared<'a> {
// last child on its own line, and compare two rewrites to choose which is
// better.
let last_shape = child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)?;
match last.rewrite(context, last_shape) {
Some(ref new_rw) if !could_fit_single_line => {
.sub_width(shape.rhs_overhead(context.config) + last.tries)
.max_width_error(child_shape.width, last.span)?;
match last.rewrite_result(context, last_shape) {
Ok(ref new_rw) if !could_fit_single_line => {
last_subexpr_str = Some(new_rw.clone());
}
Some(ref new_rw) if new_rw.lines().count() >= line_count => {
Ok(ref new_rw) if new_rw.lines().count() >= line_count => {
last_subexpr_str = Some(rw);
self.fits_single_line = could_fit_single_line && all_in_one_line;
}
new_rw @ Some(..) => {
last_subexpr_str = new_rw;
Ok(new_rw) => {
last_subexpr_str = Some(new_rw);
}
_ => {
last_subexpr_str = Some(rw);
@ -747,22 +787,28 @@ impl<'a> ChainFormatterShared<'a> {
let last_shape = if context.use_block_indent() {
last_shape
} else {
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)
.max_width_error(child_shape.width, last.span)?
};
last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape));
self.rewrites.push(last_subexpr_str?);
Some(())
let last_subexpr_str =
last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?);
self.rewrites.push(last_subexpr_str);
Ok(())
}
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
let connector = if self.fits_single_line {
// Yay, we can put everything on one line.
Cow::from("")
} else {
// Use new lines.
if context.force_one_line_chain.get() {
return None;
return Err(RewriteError::ExceedsMaxWidth {
configured_width: child_shape.width,
span: self.children.last().unknown_error()?.span,
});
}
child_shape.to_string_with_newline(context.config)
};
@ -781,7 +827,7 @@ impl<'a> ChainFormatterShared<'a> {
result.push_str(rewrite);
}
Some(result)
Ok(result)
}
}
@ -806,8 +852,8 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
parent: &ChainItem,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<()> {
let mut root_rewrite: String = parent.rewrite(context, shape)?;
) -> Result<(), RewriteError> {
let mut root_rewrite: String = parent.rewrite_result(context, shape)?;
let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
@ -817,10 +863,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
if let ChainItemKind::Comment(..) = item.kind {
break;
}
let shape = shape.offset_left(root_rewrite.len())?;
match &item.rewrite(context, shape) {
Some(rewrite) => root_rewrite.push_str(rewrite),
None => break,
let shape = shape
.offset_left(root_rewrite.len())
.max_width_error(shape.width, item.span)?;
match &item.rewrite_result(context, shape) {
Ok(rewrite) => root_rewrite.push_str(rewrite),
Err(_) => break,
}
root_ends_with_block = last_line_extendable(&root_rewrite);
@ -832,7 +880,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
}
self.shared.rewrites.push(root_rewrite);
self.root_ends_with_block = root_ends_with_block;
Some(())
Ok(())
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@ -840,7 +888,11 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
Some(get_block_child_shape(block_end, context, shape))
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
self.shared.format_children(context, child_shape)
}
@ -849,12 +901,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
context: &RewriteContext<'_>,
shape: Shape,
child_shape: Shape,
) -> Option<()> {
) -> Result<(), RewriteError> {
self.shared
.format_last_child(true, context, shape, child_shape)
}
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
self.shared.join_rewrites(context, child_shape)
}
@ -885,9 +937,9 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
parent: &ChainItem,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<()> {
) -> Result<(), RewriteError> {
let parent_shape = shape.visual_indent(0);
let mut root_rewrite = parent.rewrite(context, parent_shape)?;
let mut root_rewrite = parent.rewrite_result(context, parent_shape)?;
let multiline = root_rewrite.contains('\n');
self.offset = if multiline {
last_line_width(&root_rewrite).saturating_sub(shape.used_width())
@ -899,18 +951,19 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
let item = &self.shared.children[0];
if let ChainItemKind::Comment(..) = item.kind {
self.shared.rewrites.push(root_rewrite);
return Some(());
return Ok(());
}
let child_shape = parent_shape
.visual_indent(self.offset)
.sub_width(self.offset)?;
let rewrite = item.rewrite(context, child_shape)?;
.sub_width(self.offset)
.max_width_error(parent_shape.width, item.span)?;
let rewrite = item.rewrite_result(context, child_shape)?;
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)?;
let rewrite = item.rewrite_result(context, parent_shape)?;
root_rewrite.push_str(&rewrite);
self.offset = 0;
}
@ -919,7 +972,7 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
}
self.shared.rewrites.push(root_rewrite);
Some(())
Ok(())
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@ -932,7 +985,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
)
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
self.shared.format_children(context, child_shape)
}
@ -941,12 +998,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
context: &RewriteContext<'_>,
shape: Shape,
child_shape: Shape,
) -> Option<()> {
) -> Result<(), RewriteError> {
self.shared
.format_last_child(false, context, shape, child_shape)
}
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
self.shared.join_rewrites(context, child_shape)
}

View file

@ -4,17 +4,17 @@ use thin_vec::thin_vec;
use tracing::debug;
use crate::attr::get_attrs_from_stmt;
use crate::config::StyleEdition;
use crate::config::lists::*;
use crate::config::Version;
use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond};
use crate::items::{span_hi_for_param, span_lo_for_param};
use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
use crate::overflow::OverflowableItem;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::types::rewrite_bound_params;
use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt};
use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, stmt_expr};
// This module is pretty messy because of the rules around closures and blocks:
// FIXME - the below is probably no longer true in full.
@ -37,7 +37,7 @@ pub(crate) fn rewrite_closure(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
debug!("rewrite_closure {:?}", body);
let (prefix, extra_offset) = rewrite_closure_fn_decl(
@ -53,13 +53,15 @@ pub(crate) fn rewrite_closure(
shape,
)?;
// 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset)?;
let body_shape = shape
.offset_left(extra_offset)
.max_width_error(shape.width, span)?;
if let ast::ExprKind::Block(ref block, _) = body.kind {
// The body of the closure is an empty block.
if block.stmts.is_empty() && !block_contains_comment(context, block) {
return body
.rewrite(context, shape)
.rewrite_result(context, shape)
.map(|s| format!("{} {}", prefix, s));
}
@ -67,15 +69,15 @@ pub(crate) fn rewrite_closure(
ast::FnRetTy::Default(_) if !context.inside_macro() => {
try_rewrite_without_block(body, &prefix, context, shape, body_shape)
}
_ => None,
_ => Err(RewriteError::Unknown),
};
result.or_else(|| {
result.or_else(|_| {
// Either we require a block, or tried without and failed.
rewrite_closure_block(block, &prefix, context, body_shape)
})
} else {
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| {
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|_| {
// The closure originally had a non-block expression, but we can't fit on
// one line, so we'll insert a block.
rewrite_closure_with_block(body, &prefix, context, body_shape)
@ -89,7 +91,7 @@ fn try_rewrite_without_block(
context: &RewriteContext<'_>,
shape: Shape,
body_shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let expr = get_inner_expr(expr, prefix, context);
if is_block_closure_forced(context, expr) {
@ -153,11 +155,11 @@ fn rewrite_closure_with_block(
prefix: &str,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let left_most = left_most_sub_expr(body);
let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most);
if veto_block {
return None;
return Err(RewriteError::Unknown);
}
let block = ast::Block {
@ -185,7 +187,7 @@ fn rewrite_closure_with_block(
shape,
false,
)?;
Some(format!("{prefix} {block}"))
Ok(format!("{prefix} {block}"))
}
// Rewrite closure with a single expression without wrapping its body with block.
@ -194,7 +196,7 @@ fn rewrite_closure_expr(
prefix: &str,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
fn allow_multi_line(expr: &ast::Expr) -> bool {
match expr.kind {
ast::ExprKind::Match(..)
@ -217,12 +219,12 @@ fn rewrite_closure_expr(
// unless it is a block-like expression or we are inside macro call.
let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro())
|| context.config.force_multiline_blocks();
expr.rewrite(context, shape)
expr.rewrite_result(context, shape)
.and_then(|rw| {
if veto_multiline && rw.contains('\n') {
None
Err(RewriteError::Unknown)
} else {
Some(rw)
Ok(rw)
}
})
.map(|rw| format!("{} {}", prefix, rw))
@ -234,8 +236,12 @@ fn rewrite_closure_block(
prefix: &str,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
Some(format!("{} {}", prefix, block.rewrite(context, shape)?))
) -> RewriteResult {
Ok(format!(
"{} {}",
prefix,
block.rewrite_result(context, shape)?
))
}
// Return type is (prefix, extra_offset)
@ -250,13 +256,14 @@ fn rewrite_closure_fn_decl(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<(String, usize)> {
) -> Result<(String, usize), RewriteError> {
let binder = match binder {
ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => {
"for<> ".to_owned()
}
ast::ClosureBinder::For { generic_params, .. } => {
let lifetime_str = rewrite_bound_params(context, shape, generic_params)?;
let lifetime_str =
rewrite_bound_params(context, shape, generic_params).unknown_error()?;
format!("for<{lifetime_str}> ")
}
ast::ClosureBinder::NotPresent => "".to_owned(),
@ -287,13 +294,17 @@ fn rewrite_closure_fn_decl(
// 4 = "|| {".len(), which is overconservative when the closure consists of
// a single expression.
let nested_shape = shape
.shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())?
.sub_width(4)?;
.shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())
.and_then(|shape| shape.sub_width(4))
.max_width_error(shape.width, span)?;
// 1 = |
let param_offset = nested_shape.indent + 1;
let param_shape = nested_shape.offset_left(1)?.visual_indent(0);
let ret_str = fn_decl.output.rewrite(context, param_shape)?;
let param_shape = nested_shape
.offset_left(1)
.max_width_error(nested_shape.width, span)?
.visual_indent(0);
let ret_str = fn_decl.output.rewrite_result(context, param_shape)?;
let param_items = itemize_list(
context.snippet_provider,
@ -302,7 +313,7 @@ fn rewrite_closure_fn_decl(
",",
|param| span_lo_for_param(param),
|param| span_hi_for_param(context, param),
|param| param.rewrite(context, param_shape),
|param| param.rewrite_result(context, param_shape),
context.snippet_provider.span_after(span, "|"),
body.span.lo(),
false,
@ -317,7 +328,9 @@ fn rewrite_closure_fn_decl(
horizontal_budget,
);
let param_shape = match tactic {
DefinitiveListTactic::Horizontal => param_shape.sub_width(ret_str.len() + 1)?,
DefinitiveListTactic::Horizontal => param_shape
.sub_width(ret_str.len() + 1)
.max_width_error(param_shape.width, span)?,
_ => param_shape,
};
@ -339,7 +352,7 @@ fn rewrite_closure_fn_decl(
// 1 = space between `|...|` and body.
let extra_offset = last_line_width(&prefix) + 1;
Some((prefix, extra_offset))
Ok((prefix, extra_offset))
}
// Rewriting closure which is placed at the end of the function call's arg.
@ -348,7 +361,7 @@ pub(crate) fn rewrite_last_closure(
context: &RewriteContext<'_>,
expr: &ast::Expr,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
if let ast::ExprKind::Closure(ref closure) = expr.kind {
let ast::Closure {
ref binder,
@ -385,10 +398,12 @@ pub(crate) fn rewrite_last_closure(
)?;
// If the closure goes multi line before its body, do not overflow the closure.
if prefix.contains('\n') {
return None;
return Err(RewriteError::Unknown);
}
let body_shape = shape.offset_left(extra_offset)?;
let body_shape = shape
.offset_left(extra_offset)
.max_width_error(shape.width, expr.span)?;
// We force to use block for the body of the closure for certain kinds of expressions.
if is_block_closure_forced(context, body) {
@ -400,7 +415,7 @@ pub(crate) fn rewrite_last_closure(
// closure. However, if the closure has a return type, then we must
// keep the blocks.
match rewrite_closure_expr(body, &prefix, context, shape) {
Some(single_line_body_str)
Ok(single_line_body_str)
if !single_line_body_str.contains('\n') =>
{
single_line_body_str
@ -424,9 +439,9 @@ pub(crate) fn rewrite_last_closure(
}
// Seems fine, just format the closure in usual manner.
return expr.rewrite(context, shape);
return expr.rewrite_result(context, shape);
}
None
Err(RewriteError::Unknown)
}
/// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`.
@ -443,18 +458,18 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo
if context.inside_macro() {
false
} else {
is_block_closure_forced_inner(expr, context.config.version())
is_block_closure_forced_inner(expr, context.config.style_edition())
}
}
fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool {
fn is_block_closure_forced_inner(expr: &ast::Expr, style_edition: StyleEdition) -> bool {
match expr.kind {
ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true,
ast::ExprKind::Loop(..) if version == Version::Two => true,
ast::ExprKind::Loop(..) if style_edition >= StyleEdition::Edition2024 => true,
ast::ExprKind::AddrOf(_, _, ref expr)
| ast::ExprKind::Try(ref expr)
| ast::ExprKind::Unary(_, ref expr)
| ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version),
| ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, style_edition),
_ => false,
}
}

View file

@ -2,14 +2,14 @@
use std::{borrow::Cow, iter};
use itertools::{multipeek, MultiPeek};
use itertools::{MultiPeek, multipeek};
use rustc_span::Span;
use tracing::{debug, trace};
use crate::config::Config;
use crate::rewrite::RewriteContext;
use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::{Indent, Shape};
use crate::string::{rewrite_string, StringFormat};
use crate::string::{StringFormat, rewrite_string};
use crate::utils::{
count_newlines, first_line_width, last_line_width, trim_left_preserve_layout,
trimmed_last_line_width, unicode_str_width,
@ -158,7 +158,7 @@ pub(crate) fn combine_strs_with_missing_comments(
span: Span,
shape: Shape,
allow_extend: bool,
) -> Option<String> {
) -> RewriteResult {
trace!(
"combine_strs_with_missing_comments `{}` `{}` {:?} {:?}",
prev_str, next_str, span, shape
@ -188,7 +188,7 @@ pub(crate) fn combine_strs_with_missing_comments(
result.push_str(&indent.to_string_with_newline(config))
}
result.push_str(next_str);
return Some(result);
return Ok(result);
}
// We have a missing comment between the first expression and the second expression.
@ -233,10 +233,10 @@ pub(crate) fn combine_strs_with_missing_comments(
result.push_str(&second_sep);
result.push_str(next_str);
Some(result)
Ok(result)
}
pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> {
pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> RewriteResult {
identify_comment(orig, false, shape, config, true)
}
@ -245,7 +245,7 @@ pub(crate) fn rewrite_comment(
block_style: bool,
shape: Shape,
config: &Config,
) -> Option<String> {
) -> RewriteResult {
identify_comment(orig, block_style, shape, config, false)
}
@ -255,7 +255,7 @@ fn identify_comment(
shape: Shape,
config: &Config,
is_doc_comment: bool,
) -> Option<String> {
) -> RewriteResult {
let style = comment_style(orig, false);
// Computes the byte length of line taking into account a newline if the line is part of a
@ -347,7 +347,7 @@ fn identify_comment(
let (first_group, rest) = orig.split_at(first_group_ending);
let rewritten_first_group =
if !config.normalize_comments() && has_bare_lines && style.is_block_comment() {
trim_left_preserve_layout(first_group, shape.indent, config)?
trim_left_preserve_layout(first_group, shape.indent, config).unknown_error()?
} else if !config.normalize_comments()
&& !config.wrap_comments()
&& !(
@ -368,7 +368,7 @@ fn identify_comment(
)?
};
if rest.is_empty() {
Some(rewritten_first_group)
Ok(rewritten_first_group)
} else {
identify_comment(
rest.trim_start(),
@ -534,10 +534,11 @@ impl ItemizedBlock {
/// Returns the block as a string, with each line trimmed at the start.
fn trimmed_block_as_string(&self) -> String {
self.lines
.iter()
.map(|line| format!("{} ", line.trim_start()))
.collect::<String>()
self.lines.iter().fold(String::new(), |mut acc, line| {
acc.push_str(line.trim_start());
acc.push(' ');
acc
})
}
/// Returns the block as a string under its original form.
@ -900,7 +901,7 @@ fn rewrite_comment_inner(
shape: Shape,
config: &Config,
is_doc_comment: bool,
) -> Option<String> {
) -> RewriteResult {
let mut rewriter = CommentRewrite::new(orig, block_style, shape, config);
let line_breaks = count_newlines(orig.trim_end());
@ -934,7 +935,7 @@ fn rewrite_comment_inner(
}
}
Some(rewriter.finish())
Ok(rewriter.finish())
}
const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### ";
@ -999,7 +1000,7 @@ pub(crate) fn rewrite_missing_comment(
span: Span,
shape: Shape,
context: &RewriteContext<'_>,
) -> Option<String> {
) -> RewriteResult {
let missing_snippet = context.snippet(span);
let trimmed_snippet = missing_snippet.trim();
// check the span starts with a comment
@ -1007,7 +1008,7 @@ pub(crate) fn rewrite_missing_comment(
if !trimmed_snippet.is_empty() && pos.is_some() {
rewrite_comment(trimmed_snippet, false, shape, context.config)
} else {
Some(String::new())
Ok(String::new())
}
}
@ -1019,13 +1020,13 @@ pub(crate) fn recover_missing_comment_in_span(
shape: Shape,
context: &RewriteContext<'_>,
used_width: usize,
) -> Option<String> {
) -> RewriteResult {
let missing_comment = rewrite_missing_comment(span, shape, context)?;
if missing_comment.is_empty() {
Some(String::new())
Ok(String::new())
} else {
let missing_snippet = context.snippet(span);
let pos = missing_snippet.find('/')?;
let pos = missing_snippet.find('/').unknown_error()?;
// 1 = ` `
let total_width = missing_comment.len() + used_width + 1;
let force_new_line_before_comment =
@ -1035,7 +1036,7 @@ pub(crate) fn recover_missing_comment_in_span(
} else {
Cow::from(" ")
};
Some(format!("{sep}{missing_comment}"))
Ok(format!("{sep}{missing_comment}"))
}
}
@ -1704,12 +1705,11 @@ impl<'a> Iterator for CommentCodeSlices<'a> {
}
/// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text
/// (if it fits in the width/offset, else return `None`), else return `new`
pub(crate) fn recover_comment_removed(
new: String,
span: Span,
context: &RewriteContext<'_>,
) -> Option<String> {
) -> String {
let snippet = context.snippet(span);
if snippet != new && changed_comment_content(snippet, &new) {
// We missed some comments. Warn and keep the original text.
@ -1723,9 +1723,9 @@ pub(crate) fn recover_comment_removed(
)],
);
}
Some(snippet.to_owned())
snippet.to_owned()
} else {
Some(new)
new
}
}

View file

@ -70,15 +70,15 @@ macro_rules! create_config {
//
// - $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 ),+ );+ $(;)*) => (
($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[cfg(test)]
use std::collections::HashSet;
use std::io::Write;
use serde::{Deserialize, Serialize};
use $crate::config::style_edition::StyleEditionDefault;
#[derive(Clone)]
#[allow(unreachable_pub)]
@ -89,7 +89,10 @@ macro_rules! create_config {
// - 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)),+
// - 4: true if the option was set manually from a CLI flag
// FIXME: 4 is probably unnecessary and duplicative
// https://github.com/rust-lang/rustfmt/issues/6252
$($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+
}
// Just like the Config struct but with each property wrapped
@ -100,7 +103,7 @@ macro_rules! create_config {
#[derive(Deserialize, Serialize, Clone)]
#[allow(unreachable_pub)]
pub struct PartialConfig {
$(pub $i: Option<$ty>),+
$(pub $i: Option<<$ty as StyleEditionDefault>::ConfigType>),+
}
// Macro hygiene won't allow us to make `set_$i()` methods on Config
@ -114,7 +117,7 @@ macro_rules! create_config {
impl<'a> ConfigSetter<'a> {
$(
#[allow(unreachable_pub)]
pub fn $i(&mut self, value: $ty) {
pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
(self.0).$i.2 = value;
match stringify!($i) {
"max_width"
@ -130,6 +133,37 @@ macro_rules! create_config {
"merge_imports" => self.0.set_merge_imports(),
"fn_args_layout" => self.0.set_fn_args_layout(),
"hide_parse_errors" => self.0.set_hide_parse_errors(),
"version" => self.0.set_version(),
&_ => (),
}
}
)+
}
#[allow(unreachable_pub)]
pub struct CliConfigSetter<'a>(&'a mut Config);
impl<'a> CliConfigSetter<'a> {
$(
#[allow(unreachable_pub)]
pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
(self.0).$i.2 = value;
(self.0).$i.4 = true;
match stringify!($i) {
"max_width"
| "use_small_heuristics"
| "fn_call_width"
| "single_line_if_else_max_width"
| "single_line_let_else_max_width"
| "attr_fn_like_width"
| "struct_lit_width"
| "struct_variant_width"
| "array_width"
| "chain_width" => self.0.set_heuristics(),
"merge_imports" => self.0.set_merge_imports(),
"fn_args_layout" => self.0.set_fn_args_layout(),
"hide_parse_errors" => self.0.set_hide_parse_errors(),
"version" => self.0.set_version(),
&_ => (),
}
}
@ -150,25 +184,66 @@ macro_rules! create_config {
)+
}
// Query each option, returns true if the user set the option via a CLI flag,
// false if a default was used.
#[allow(unreachable_pub)]
pub struct CliConfigWasSet<'a>(&'a Config);
impl<'a> CliConfigWasSet<'a> {
$(
#[allow(unreachable_pub)]
pub fn $i(&self) -> bool {
(self.0).$i.4
}
)+
}
impl Config {
$(
#[allow(unreachable_pub)]
pub fn $i(&self) -> $ty {
pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType {
self.$i.0.set(true);
self.$i.2.clone()
}
)+
#[allow(unreachable_pub)]
pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config {
Config {
$(
$i: (
Cell::new(false),
false,
<$ty as StyleEditionDefault>::style_edition_default(
style_edition
),
$stb,
false,
),
)+
}
}
#[allow(unreachable_pub)]
pub fn set(&mut self) -> ConfigSetter<'_> {
ConfigSetter(self)
}
#[allow(unreachable_pub)]
pub fn set_cli(&mut self) -> CliConfigSetter<'_> {
CliConfigSetter(self)
}
#[allow(unreachable_pub)]
pub fn was_set(&self) -> ConfigWasSet<'_> {
ConfigWasSet(self)
}
#[allow(unreachable_pub)]
pub fn was_set_cli(&self) -> CliConfigWasSet<'_> {
CliConfigWasSet(self)
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$(
if let Some(option_value) = parsed.$i {
@ -186,6 +261,7 @@ macro_rules! create_config {
self.set_merge_imports();
self.set_fn_args_layout();
self.set_hide_parse_errors();
self.set_version();
self
}
@ -212,7 +288,9 @@ macro_rules! create_config {
pub fn is_valid_key_val(key: &str, val: &str) -> bool {
match key {
$(
stringify!($i) => val.parse::<$ty>().is_ok(),
stringify!($i) => {
val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok()
}
)+
_ => false,
}
@ -246,11 +324,15 @@ macro_rules! create_config {
match key {
$(
stringify!($i) => {
let option_value = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!($ty)));
let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>()
.expect(
&format!(
"Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!(<$ty as StyleEditionDefault>::ConfigType)
)
);
// Users are currently allowed to set unstable
// options/variants via the `--config` options override.
@ -261,7 +343,7 @@ macro_rules! create_config {
// 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;
self.$i.2 = value;
}
)+
_ => panic!("Unknown config key in override: {}", key)
@ -281,6 +363,7 @@ macro_rules! create_config {
"merge_imports" => self.set_merge_imports(),
"fn_args_layout" => self.set_fn_args_layout(),
"hide_parse_errors" => self.set_hide_parse_errors(),
"version" => self.set_version(),
&_ => (),
}
}
@ -301,6 +384,7 @@ macro_rules! create_config {
#[allow(unreachable_pub)]
pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
let style_edition = StyleEdition::Edition2015;
use std::cmp;
let max = 0;
$( let max = cmp::max(max, stringify!($i).len()+1); )+
@ -317,14 +401,17 @@ macro_rules! create_config {
}
name_out.push_str(name_raw);
name_out.push(' ');
let mut default_str = format!("{}", $def);
let default_value = <$ty as StyleEditionDefault>::style_edition_default(
style_edition
);
let mut default_str = format!("{}", default_value);
if default_str.is_empty() {
default_str = String::from("\"\"");
}
writeln!(out,
"{}{} Default: {}{}",
name_out,
<$ty>::doc_hint(),
<<$ty as StyleEditionDefault>::ConfigType>::doc_hint(),
default_str,
if !$stb { " (unstable)" } else { "" }).unwrap();
$(
@ -477,12 +564,36 @@ macro_rules! create_config {
}
}
fn set_version(&mut self) {
if !self.was_set().version() {
return;
}
eprintln!(
"Warning: the `version` option is deprecated. \
Use `style_edition` instead."
);
if self.was_set().style_edition() || self.was_set_cli().style_edition() {
eprintln!(
"Warning: the deprecated `version` option was \
used in conjunction with the `style_edition` \
option which takes precedence. \
The value of the `version` option will be ignored."
);
}
}
#[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 {
let style_edition = StyleEdition::Edition2015;
$(
let default_value = <$ty as StyleEditionDefault>::style_edition_default(
style_edition
);
if let stringify!($i) = key {
return self.$i.1 && self.$i.2 == $def;
return self.$i.1 && self.$i.2 == default_value;
}
)+
false
@ -492,11 +603,7 @@ macro_rules! create_config {
// Template for the default configuration
impl Default for Config {
fn default() -> Config {
Config {
$(
$i: (Cell::new(false), false, $def, $stb),
)+
}
Config::default_with_style_edition(StyleEdition::Edition2015)
}
}
)

View file

@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str};
use rustc_data_structures::sync::Lrc;
use rustc_span::SourceFile;
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser};
use serde_json as json;
use thiserror::Error;
@ -201,7 +201,7 @@ impl FileLines {
}
/// Returns `true` if this `FileLines` contains all lines in all files.
pub(crate) fn is_all(&self) -> bool {
pub fn is_all(&self) -> bool {
self.0.is_none()
}

View file

@ -10,9 +10,7 @@ use crate::config::config_type::ConfigType;
#[allow(unreachable_pub)]
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};
pub use crate::config::macro_names::MacroSelector;
#[allow(unreachable_pub)]
pub use crate::config::options::*;
@ -34,161 +32,169 @@ pub(crate) mod style_edition;
// `name: value type, default value, is stable, description;`
create_config! {
// Fundamental stuff
max_width: usize, 100, true, "Maximum width of each line";
hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
tab_spaces: usize, 4, true, "Number of spaces per tab";
newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings";
indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items";
max_width: MaxWidth, true, "Maximum width of each line";
hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment";
tab_spaces: TabSpaces, true, "Number of spaces per tab";
newline_style: NewlineStyleConfig, true, "Unix or Windows line endings";
indent_style: IndentStyleConfig, false, "How do we indent expressions or items";
// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \
use_small_heuristics: UseSmallHeuristics, true, "Whether to use different \
formatting for items and expressions if they satisfy a heuristic notion of 'small'";
width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
"'small' heuristic values";
fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
falling back to vertical formatting.";
attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \
attributes before falling back to vertical formatting.";
struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \
falling back to vertical formatting.";
struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \
falling back to vertical formatting.";
array_width: usize, 60, true, "Maximum width of an array literal before falling \
struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \
before falling back to vertical formatting.";
array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
back to vertical formatting.";
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \
expressions. A value of zero means always break if-else expressions.";
single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \
let-else statements. A value of zero means always format the divergent `else` block \
over multiple lines.";
chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \
line if-else expressions. A value of zero means always break if-else expressions.";
single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \
single line let-else statements. A value of zero means always format the divergent `else` \
block over multiple lines.";
// Comments. macros, and strings
wrap_comments: bool, false, false, "Break comments to fit on the line";
format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments.";
doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \
comments. No effect unless format_code_in_doc_comments = true";
comment_width: usize, 80, false,
wrap_comments: WrapComments, false, "Break comments to fit on the line";
format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \
doc comments.";
doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \
snippets in doc comments. No effect unless format_code_in_doc_comments = true";
comment_width: CommentWidth, false,
"Maximum length of comments. No effect unless wrap_comments = true";
normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments";
format_strings: bool, false, false, "Format string literals where necessary";
format_macro_matchers: bool, false, false,
normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \
possible";
normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \
comments";
format_strings: FormatStrings, false, "Format string literals where necessary";
format_macro_matchers: FormatMacroMatchers, false,
"Format the metavariable matching patterns in macros";
format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions";
skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false,
format_macro_bodies: FormatMacroBodies, false,
"Format the bodies of declarative macro definitions";
skip_macro_invocations: SkipMacroInvocations, false,
"Skip formatting the bodies of macros invoked with the following names.";
hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
"Format hexadecimal integer literals";
hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals";
// Single line expressions and items
empty_item_single_line: bool, true, false,
empty_item_single_line: EmptyItemSingleLine, false,
"Put empty-body functions and impls on a single line";
struct_lit_single_line: bool, true, false,
struct_lit_single_line: StructLitSingleLine, false,
"Put small struct literals on a single line";
fn_single_line: bool, false, false, "Put single-expression functions on a single line";
where_single_line: bool, false, false, "Force where-clauses to be on a single line";
fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line";
where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line";
// Imports
imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports";
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
imports_indent: ImportsIndent, false, "Indent of imports";
imports_layout: ImportsLayout, false, "Item layout inside a import block";
imports_granularity: ImportsGranularityConfig, false,
"Merge or split imports to the provided granularity";
group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false,
group_imports: GroupImportsTacticConfig, false,
"Controls the strategy for how imports are grouped together";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
// Ordering
reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically";
reorder_modules: bool, true, true, "Reorder module statements alphabetically in group";
reorder_impl_items: bool, false, false, "Reorder impl items";
reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \
alphabetically";
reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group";
reorder_impl_items: ReorderImplItems, false, "Reorder impl items";
// Spaces around punctuation
type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
type_punctuation_density: TypePunctuationDensity, false,
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
space_before_colon: bool, false, false, "Leave a space before the colon";
space_after_colon: bool, true, false, "Leave a space after the colon";
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators";
binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
space_before_colon: SpaceBeforeColon, false, "Leave a space before the colon";
space_after_colon: SpaceAfterColon, false, "Leave a space after the colon";
spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the .. and ..= range \
operators";
binop_separator: BinopSeparator, false,
"Where to put a binary operator when a binary expression goes multiline";
// Misc.
remove_nested_parens: bool, true, true, "Remove nested parens";
combine_control_expr: bool, true, false, "Combine control expressions with function calls";
short_array_element_width_threshold: usize, 10, true,
remove_nested_parens: RemoveNestedParens, true, "Remove nested parens";
combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \
calls";
short_array_element_width_threshold: ShortArrayElementWidthThreshold, true,
"Width threshold for an array element to be considered short";
overflow_delimited_expr: bool, false, false,
overflow_delimited_expr: OverflowDelimitedExpr, false,
"Allow trailing bracket/brace delimited expressions to overflow";
struct_field_align_threshold: usize, 0, false,
struct_field_align_threshold: StructFieldAlignThreshold, false,
"Align struct fields if their diffs fits within threshold";
enum_discrim_align_threshold: usize, 0, false,
enum_discrim_align_threshold: EnumDiscrimAlignThreshold, false,
"Align enum variants discrims, if their diffs fit within threshold";
match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
the same line with the pattern of arms";
match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true,
match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \
on the same line with the pattern of arms";
match_arm_leading_pipes: MatchArmLeadingPipeConfig, true,
"Determines whether leading pipes are emitted on match arms";
force_multiline_blocks: bool, false, false,
force_multiline_blocks: ForceMultilineBlocks, false,
"Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_layout: Density, Density::Tall, true,
fn_args_layout: FnArgsLayout, true,
"(deprecated: use fn_params_layout instead)";
fn_params_layout: Density, Density::Tall, true,
fn_params_layout: FnParamsLayout, 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: BraceStyleConfig, false, "Brace style for items";
control_brace_style: ControlBraceStyleConfig, false,
"Brace style for control flow constructs";
trailing_semicolon: bool, true, false,
trailing_semicolon: TrailingSemicolon, false,
"Add trailing semicolon after break, continue and return";
trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
trailing_comma: TrailingComma, false,
"How to handle trailing commas for lists";
match_block_trailing_comma: bool, false, true,
match_block_trailing_comma: MatchBlockTrailingComma, true,
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
blank_lines_upper_bound: usize, 1, false,
blank_lines_upper_bound: BlankLinesUpperBound, false,
"Maximum number of blank lines which can be put between items";
blank_lines_lower_bound: usize, 0, false,
blank_lines_lower_bound: BlankLinesLowerBound, false,
"Minimum number of blank lines which must be put between items";
edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)";
version: Version, Version::One, false, "Version of formatting rules";
inline_attribute_width: usize, 0, false,
edition: EditionConfig, true, "The edition of the parser (RFC 2052)";
style_edition: StyleEditionConfig, false, "The edition of the Style Guide (RFC 3338)";
version: VersionConfig, false, "Version of formatting rules";
inline_attribute_width: InlineAttributeWidth, false,
"Write an item and its attribute on the same line \
if their combined width is below a threshold";
format_generated_files: bool, true, false, "Format generated files";
generated_marker_line_search_limit: usize, 5, false, "Number of lines to check for a \
`@generated` marker when `format_generated_files` is enabled";
format_generated_files: FormatGeneratedFiles, false, "Format generated files";
generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \
check for a `@generated` marker when `format_generated_files` is enabled";
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand";
use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible";
force_explicit_abi: bool, true, true, "Always print the abi for extern items";
condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
in tuple patterns";
merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one";
use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand";
use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \
possible";
force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items";
condense_wildcard_suffixes: CondenseWildcardSuffixes, false, "Replace strings of _ wildcards \
by a single .. in tuple patterns";
// Control options (changes the operation of rustfmt, rather than the formatting)
color: Color, Color::Auto, false,
color: ColorConfig, false,
"What Color option to use when none is supplied: Always, Never, Auto";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
required_version: RequiredVersion, false,
"Require a specific version of rustfmt";
unstable_features: bool, false, false,
unstable_features: UnstableFeatures, false,
"Enables unstable features. Only available on nightly channel";
disable_all_formatting: bool, false, true, "Don't reformat anything";
skip_children: bool, false, false, "Don't reformat out of line modules";
hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)";
show_parse_errors: bool, true, false, "Show errors from the parser (unstable)";
error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width";
error_on_unformatted: bool, false, false,
disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything";
skip_children: SkipChildren, false, "Don't reformat out of line modules";
hide_parse_errors: HideParseErrors, false, "Hide errors from the parser";
show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)";
error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \
max_width";
error_on_unformatted: ErrorOnUnformatted, false,
"Error if unable to get comments or string literals within max_width, \
or they are left with trailing whitespaces";
ignore: IgnoreList, IgnoreList::default(), false,
ignore: Ignore, false,
"Skip formatting the specified files and directories";
// Not user-facing
verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user";
file_lines: FileLines, FileLines::all(), false,
verbose: Verbose, false, "How much to information to emit to the user";
file_lines: FileLinesConfig, false,
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option";
emit_mode: EmitMode, EmitMode::Files, false,
emit_mode: EmitModeConfig, false,
"What emit Mode to use when none is supplied";
make_backup: bool, false, false, "Backup changed files";
print_misformatted_file_names: bool, false, true,
make_backup: MakeBackup, false, "Backup changed files";
print_misformatted_file_names: PrintMisformattedFileNames, true,
"Prints the names of mismatched files that were formatted. Prints the names of \
files that would be formatted when used with `--check` mode. ";
}
@ -211,9 +217,48 @@ impl PartialConfig {
::toml::to_string(&cloned).map_err(ToTomlError)
}
pub(super) fn to_parsed_config(
self,
style_edition_override: Option<StyleEdition>,
edition_override: Option<Edition>,
version_override: Option<Version>,
dir: &Path,
) -> Config {
Config::default_for_possible_style_edition(
style_edition_override.or(self.style_edition),
edition_override.or(self.edition),
version_override.or(self.version),
)
.fill_from_parsed_config(self, dir)
}
}
impl Config {
pub fn default_for_possible_style_edition(
style_edition: Option<StyleEdition>,
edition: Option<Edition>,
version: Option<Version>,
) -> Config {
// Ensures the configuration defaults associated with Style Editions
// follow the precedence set in
// https://rust-lang.github.io/rfcs/3338-style-evolution.html
// 'version' is a legacy alias for 'style_edition' that we'll support
// for some period of time
// FIXME(calebcartwright) - remove 'version' at some point
match (style_edition, version, edition) {
(Some(se), _, _) => Self::default_with_style_edition(se),
(None, Some(Version::Two), _) => {
Self::default_with_style_edition(StyleEdition::Edition2024)
}
(None, Some(Version::One), _) => {
Self::default_with_style_edition(StyleEdition::Edition2015)
}
(None, None, Some(e)) => Self::default_with_style_edition(e.into()),
(None, None, None) => Config::default(),
}
}
pub(crate) fn version_meets_requirement(&self) -> bool {
if self.was_set().required_version() {
let version = env!("CARGO_PKG_VERSION");
@ -237,12 +282,23 @@ impl Config {
///
/// Returns a `Config` if the config could be read and parsed from
/// the file, otherwise errors.
pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
pub(super) fn from_toml_path(
file_path: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<Config, Error> {
let mut file = File::open(&file_path)?;
let mut toml = String::new();
file.read_to_string(&mut toml)?;
Config::from_toml(&toml, file_path.parent().unwrap())
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
Config::from_toml_for_style_edition(
&toml,
file_path.parent().unwrap(),
edition,
style_edition,
version,
)
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
}
/// Resolves the config for input in `dir`.
@ -254,9 +310,14 @@ impl Config {
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
pub(super) fn from_resolved_toml_path(
dir: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<(Config, Option<PathBuf>), Error> {
/// Try to find a project file in the given directory and its parents.
/// Returns the path of a the nearest project file if one exists,
/// Returns the path of the nearest project file if one exists,
/// or `None` if no project file was found.
fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
let mut current = if dir.is_relative() {
@ -299,12 +360,27 @@ impl Config {
}
match resolve_project_file(dir)? {
None => Ok((Config::default(), None)),
Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
None => Ok((
Config::default_for_possible_style_edition(style_edition, edition, version),
None,
)),
Some(path) => Config::from_toml_path(&path, edition, style_edition, version)
.map(|config| (config, Some(path))),
}
}
pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
#[allow(dead_code)]
pub(super) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
Self::from_toml_for_style_edition(toml, dir, None, None, None)
}
pub(crate) fn from_toml_for_style_edition(
toml: &str,
dir: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<Config, String> {
let parsed: ::toml::Value = toml
.parse()
.map_err(|e| format!("Could not parse TOML: {}", e))?;
@ -318,12 +394,13 @@ impl Config {
err.push_str(msg)
}
}
match parsed.try_into() {
match parsed.try_into::<PartialConfig>() {
Ok(parsed_config) => {
if !err.is_empty() {
eprint!("{err}");
}
Ok(Config::default().fill_from_parsed_config(parsed_config, dir))
Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir))
}
Err(e) => {
err.push_str("Error: Decoding config file failed:\n");
@ -341,17 +418,26 @@ pub fn load_config<O: CliOptions>(
file_path: Option<&Path>,
options: Option<O>,
) -> Result<(Config, Option<PathBuf>), Error> {
let over_ride = match options {
Some(ref opts) => config_path(opts)?,
None => None,
let (over_ride, edition, style_edition, version) = match options {
Some(ref opts) => (
config_path(opts)?,
opts.edition(),
opts.style_edition(),
opts.version(),
),
None => (None, None, None, None),
};
let result = if let Some(over_ride) = over_ride {
Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned())))
Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version)
.map(|p| (p, Some(over_ride.to_owned())))
} else if let Some(file_path) = file_path {
Config::from_resolved_toml_path(file_path)
Config::from_resolved_toml_path(file_path, edition, style_edition, version)
} else {
Ok((Config::default(), None))
Ok((
Config::default_for_possible_style_edition(style_edition, edition, version),
None,
))
};
result.map(|(mut c, p)| {
@ -362,7 +448,7 @@ pub fn load_config<O: CliOptions>(
})
}
// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
// Check for the presence of known config file names (`rustfmt.toml`, `.rustfmt.toml`) in `dir`
//
// Return the path if a config file exists, empty if no file exists, and Error for IO errors
fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
match fs::metadata(&config_file) {
// Only return if it's a file to handle the unlikely situation of a directory named
// `rustfmt.toml`.
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
Err(e) => {
@ -411,7 +497,11 @@ fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> {
config_path_not_found(path.to_str().unwrap())
}
}
path => Ok(path.map(ToOwned::to_owned)),
Some(path) => Ok(Some(
// Canonicalize only after checking above that the `path.exists()`.
path.canonicalize()?,
)),
None => Ok(None),
}
}
@ -420,12 +510,13 @@ mod test {
use super::*;
use std::str;
use crate::config::macro_names::MacroName;
use crate::config::macro_names::{MacroName, MacroSelectors};
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
#[allow(dead_code)]
mod mock {
use super::super::*;
use crate::config_option_with_style_edition_default;
use rustfmt_config_proc_macro::config_type;
#[config_type]
@ -436,66 +527,69 @@ mod test {
V3,
}
config_option_with_style_edition_default!(
StableOption, bool, _ => false;
UnstableOption, bool, _ => false;
PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1;
);
create_config! {
// Options that are used by the generated functions
max_width: usize, 100, true, "Maximum width of each line";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
"Require a specific version of rustfmt.";
ignore: IgnoreList, IgnoreList::default(), false,
"Skip formatting the specified files and directories.";
verbose: Verbosity, Verbosity::Normal, false,
"How much to information to emit to the user";
file_lines: FileLines, FileLines::all(), false,
max_width: MaxWidth, true, "Maximum width of each line";
required_version: RequiredVersion, false, "Require a specific version of rustfmt.";
ignore: Ignore, false, "Skip formatting the specified files and directories.";
verbose: Verbose, false, "How much to information to emit to the user";
file_lines: FileLinesConfig, false,
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option";
// merge_imports deprecation
imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
"Merge imports";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
imports_granularity: ImportsGranularityConfig, false, "Merge imports";
merge_imports: MergeImports, 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,
fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)";
fn_params_layout: FnParamsLayout, true,
"Control the layout of parameters in a function signatures.";
// hide_parse_errors renamed to show_parse_errors
hide_parse_errors: bool, false, false,
hide_parse_errors: HideParseErrors, false,
"(deprecated: use show_parse_errors instead)";
show_parse_errors: bool, true, false,
show_parse_errors: ShowParseErrors, false,
"Show errors from the parser (unstable)";
// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true,
use_small_heuristics: UseSmallHeuristics, true,
"Whether to use different formatting for items and \
expressions if they satisfy a heuristic notion of 'small'.";
width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
"'small' heuristic values";
width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
falling back to vertical formatting.";
attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
attributes before falling back to vertical formatting.";
struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
falling back to vertical formatting.";
struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \
attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \
function-like attributes before falling back to vertical formatting.";
struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \
before falling back to vertical formatting.";
struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \
variant before falling back to vertical formatting.";
array_width: usize, 60, true, "Maximum width of an array literal before falling \
array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
back to vertical formatting.";
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \
line if-else expressions. A value of zero means always break if-else expressions.";
single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \
line let-else statements. A value of zero means always format the divergent \
`else` block over multiple lines.";
chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \
for single line if-else expressions. A value of zero means always break if-else \
expressions.";
single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \
for single line let-else statements. A value of zero means always format the \
divergent `else` block over multiple lines.";
// 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";
stable_option: StableOption, true, "A stable option";
unstable_option: UnstableOption, false, "An unstable option";
partially_unstable_option: PartiallyUnstable, true, "A partially unstable option";
edition: EditionConfig, true, "blah";
style_edition: StyleEditionConfig, true, "blah";
version: VersionConfig, false, "blah blah"
}
#[cfg(test)]
@ -679,6 +773,7 @@ match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2015"
style_edition = "2015"
version = "One"
inline_attribute_width = 0
format_generated_files = true
@ -706,6 +801,114 @@ make_backup = false
assert_eq!(&toml, &default_config);
}
#[test]
fn test_dump_style_edition_2024_config() {
let edition_2024_config = format!(
r#"max_width = 100
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
indent_style = "Block"
use_small_heuristics = "Default"
fn_call_width = 60
attr_fn_like_width = 70
struct_lit_width = 18
struct_variant_width = 35
array_width = 60
chain_width = 60
single_line_if_else_max_width = 50
single_line_let_else_max_width = 50
wrap_comments = false
format_code_in_doc_comments = false
doc_comment_code_block_width = 100
comment_width = 80
normalize_comments = false
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
fn_single_line = false
where_single_line = false
imports_indent = "Block"
imports_layout = "Mixed"
imports_granularity = "Preserve"
group_imports = "Preserve"
reorder_imports = true
reorder_modules = true
reorder_impl_items = false
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
short_array_element_width_threshold = 10
overflow_delimited_expr = true
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2015"
style_edition = "2024"
version = "Two"
inline_attribute_width = 0
format_generated_files = true
generated_marker_line_search_limit = 5
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
required_version = "{}"
unstable_features = false
disable_all_formatting = false
skip_children = false
show_parse_errors = true
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false
"#,
env!("CARGO_PKG_VERSION")
);
let toml = Config::default_with_style_edition(StyleEdition::Edition2024)
.all_options()
.to_toml()
.unwrap();
assert_eq!(&toml, &edition_2024_config);
}
#[test]
fn test_editions_2015_2018_2021_identical() {
let get_edition_toml = |style_edition: StyleEdition| {
Config::default_with_style_edition(style_edition)
.all_options()
.to_toml()
.unwrap()
};
let edition2015 = get_edition_toml(StyleEdition::Edition2015);
let edition2018 = get_edition_toml(StyleEdition::Edition2018);
let edition2021 = get_edition_toml(StyleEdition::Edition2021);
assert_eq!(edition2015, edition2018);
assert_eq!(edition2018, edition2021);
}
#[stable_only_test]
#[test]
fn test_as_not_nightly_channel() {
@ -735,6 +938,20 @@ make_backup = false
assert_eq!(config.unstable_features(), true);
}
#[test]
fn test_set_cli() {
let mut config = Config::default();
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), false);
config.set().edition(Edition::Edition2021);
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), false);
config.set_cli().edition(Edition::Edition2021);
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), true);
assert_eq!(config.was_set_cli().emit_mode(), false);
}
#[cfg(test)]
mod deprecated_option_merge_imports {
use super::*;

View file

@ -1,6 +1,6 @@
#![allow(unused_imports)]
use std::collections::{hash_set, HashSet};
use std::collections::{HashSet, hash_set};
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@ -11,8 +11,10 @@ use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::config::lists::*;
use crate::config::Config;
use crate::config::file_lines::FileLines;
use crate::config::lists::*;
use crate::config::macro_names::MacroSelectors;
#[config_type]
pub enum NewlineStyle {
@ -413,7 +415,13 @@ impl FromStr for IgnoreList {
/// values in a config with values from the command line.
pub trait CliOptions {
fn apply_to(self, config: &mut Config);
/// It is ok if the returned path doesn't exist or is not canonicalized
/// (i.e. the callers are expected to handle such cases).
fn config_path(&self) -> Option<&Path>;
fn edition(&self) -> Option<Edition>;
fn style_edition(&self) -> Option<StyleEdition>;
fn version(&self) -> Option<Version>;
}
/// The edition of the syntax and semantics of code (RFC 2052).
@ -454,6 +462,17 @@ impl From<Edition> for rustc_span::edition::Edition {
}
}
impl From<Edition> for StyleEdition {
fn from(edition: Edition) -> Self {
match edition {
Edition::Edition2015 => StyleEdition::Edition2015,
Edition::Edition2018 => StyleEdition::Edition2018,
Edition::Edition2021 => StyleEdition::Edition2021,
Edition::Edition2024 => StyleEdition::Edition2024,
}
}
}
impl PartialOrd for Edition {
fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
@ -471,10 +490,11 @@ pub enum MatchArmLeadingPipe {
Preserve,
}
/// Defines the default values for each config according to [the style guide].
/// rustfmt output may differ between style editions.
/// Defines the default values for each config according to the edition of the
/// [Style Guide] as per [RFC 3338]. Rustfmt output may differ between Style editions.
///
/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/
/// [Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
/// [RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
#[config_type]
pub enum StyleEdition {
#[value = "2015"]
@ -491,6 +511,169 @@ pub enum StyleEdition {
Edition2021,
#[value = "2024"]
#[doc_hint = "2024"]
#[unstable_variant]
/// [Edition 2024]().
Edition2024,
}
impl From<StyleEdition> for rustc_span::edition::Edition {
fn from(edition: StyleEdition) -> Self {
match edition {
StyleEdition::Edition2015 => Self::Edition2015,
StyleEdition::Edition2018 => Self::Edition2018,
StyleEdition::Edition2021 => Self::Edition2021,
StyleEdition::Edition2024 => Self::Edition2024,
}
}
}
impl PartialOrd for StyleEdition {
fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
}
}
/// Defines unit structs to implement `StyleEditionDefault` for.
#[macro_export]
macro_rules! config_option_with_style_edition_default {
($name:ident, $config_ty:ty, _ => $default:expr) => {
#[allow(unreachable_pub)]
pub struct $name;
$crate::style_edition_default!($name, $config_ty, _ => $default);
};
($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
pub struct $name;
$crate::style_edition_default!(
$name,
$config_ty,
Edition2024 => $default_2024,
_ => $default_2015
);
};
(
$($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
$(;)*
) => {
$(
config_option_with_style_edition_default!(
$name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
);
)*
};
}
// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't.
// I chose to add a `Config` suffix in cases where a type for the config option was already
// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion
// about using the `Config` suffix more consistently.
config_option_with_style_edition_default!(
// Fundamental stuff
MaxWidth, usize, _ => 100;
HardTabs, bool, _ => false;
TabSpaces, usize, _ => 4;
NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
// Width Heuristics
UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
FnCallWidth, usize, _ => 60;
AttrFnLikeWidth, usize, _ => 70;
StructLitWidth, usize, _ => 18;
StructVariantWidth, usize, _ => 35;
ArrayWidth, usize, _ => 60;
ChainWidth, usize, _ => 60;
SingleLineIfElseMaxWidth, usize, _ => 50;
SingleLineLetElseMaxWidth, usize, _ => 50;
// Comments. macros, and strings
WrapComments, bool, _ => false;
FormatCodeInDocComments, bool, _ => false;
DocCommentCodeBlockWidth, usize, _ => 100;
CommentWidth, usize, _ => 80;
NormalizeComments, bool, _ => false;
NormalizeDocAttributes, bool, _ => false;
FormatStrings, bool, _ => false;
FormatMacroMatchers, bool, _ => false;
FormatMacroBodies, bool, _ => true;
SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
// Single line expressions and items
EmptyItemSingleLine, bool, _ => true;
StructLitSingleLine, bool, _ => true;
FnSingleLine, bool, _ => false;
WhereSingleLine, bool, _ => false;
// Imports
ImportsIndent, IndentStyle, _ => IndentStyle::Block;
ImportsLayout, ListTactic, _ => ListTactic::Mixed;
ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
MergeImports, bool, _ => false;
// Ordering
ReorderImports, bool, _ => true;
ReorderModules, bool, _ => true;
ReorderImplItems, bool, _ => false;
// Spaces around punctuation
TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
SpaceBeforeColon, bool, _ => false;
SpaceAfterColon, bool, _ => true;
SpacesAroundRanges, bool, _ => false;
BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
// Misc.
RemoveNestedParens, bool, _ => true;
CombineControlExpr, bool, _ => true;
ShortArrayElementWidthThreshold, usize, _ => 10;
OverflowDelimitedExpr, bool, Edition2024 => true, _ => false;
StructFieldAlignThreshold, usize, _ => 0;
EnumDiscrimAlignThreshold, usize, _ => 0;
MatchArmBlocks, bool, _ => true;
MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
ForceMultilineBlocks, bool, _ => false;
FnArgsLayout, Density, _ => Density::Tall;
FnParamsLayout, Density, _ => Density::Tall;
BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
TrailingSemicolon, bool, _ => true;
TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
MatchBlockTrailingComma, bool, _ => false;
BlankLinesUpperBound, usize, _ => 1;
BlankLinesLowerBound, usize, _ => 0;
EditionConfig, Edition, _ => Edition::Edition2015;
StyleEditionConfig, StyleEdition,
Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
InlineAttributeWidth, usize, _ => 0;
FormatGeneratedFiles, bool, _ => true;
GeneratedMarkerLineSearchLimit, usize, _ => 5;
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
MergeDerives, bool, _ => true;
UseTryShorthand, bool, _ => false;
UseFieldInitShorthand, bool, _ => false;
ForceExplicitAbi, bool, _ => true;
CondenseWildcardSuffixes, bool, _ => false;
// Control options (changes the operation of rustfmt, rather than the formatting)
ColorConfig, Color, _ => Color::Auto;
RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
UnstableFeatures, bool, _ => false;
DisableAllFormatting, bool, _ => false;
SkipChildren, bool, _ => false;
HideParseErrors, bool, _ => false;
ShowParseErrors, bool, _ => true;
ErrorOnLineOverflow, bool, _ => false;
ErrorOnUnformatted, bool, _ => false;
Ignore, IgnoreList, _ => IgnoreList::default();
// Not user-facing
Verbose, Verbosity, _ => Verbosity::Normal;
FileLinesConfig, FileLines, _ => FileLines::all();
EmitModeConfig, EmitMode, _ => EmitMode::Files;
MakeBackup, bool, _ => false;
PrintMisformattedFileNames, bool, _ => false;
);

View file

@ -2,7 +2,7 @@ use crate::config::StyleEdition;
/// Defines the default value for the given style edition
#[allow(dead_code)]
pub(crate) trait StyleEditionDefault {
pub trait StyleEditionDefault {
type ConfigType;
fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType;
}

View file

@ -1,6 +1,6 @@
use self::xml::XmlEscaped;
use super::*;
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
mod xml;

View file

@ -1,5 +1,5 @@
use super::*;
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
use serde::Serialize;
use serde_json::to_string as to_json_string;

View file

@ -1,5 +1,5 @@
use super::*;
use crate::rustfmt_diff::{make_diff, ModifiedLines};
use crate::rustfmt_diff::{ModifiedLines, make_diff};
#[derive(Debug, Default)]
pub(crate) struct ModifiedLinesEmitter;

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError};
use crate::parse::session::ParseSess;
use crate::utils::{contains_skip, count_newlines};
use crate::visitor::FmtVisitor;
use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session};
use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file};
mod generated;
mod newline_style;

View file

@ -13,7 +13,9 @@ use rustfmt_nightly as rustfmt;
use tracing::debug;
use tracing_subscriber::EnvFilter;
use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session};
use crate::rustfmt::{
CliOptions, FormatReportFormatterBuilder, Input, Session, Version, load_config,
};
fn prune_files(files: Vec<&str>) -> Vec<&str> {
let prefixes: Vec<_> = files
@ -87,6 +89,15 @@ impl CliOptions for NullOptions {
fn config_path(&self) -> Option<&Path> {
unreachable!();
}
fn edition(&self) -> Option<rustfmt_nightly::Edition> {
unreachable!();
}
fn style_edition(&self) -> Option<rustfmt_nightly::StyleEdition> {
unreachable!();
}
fn version(&self) -> Option<Version> {
unreachable!();
}
}
fn uncommitted_files() -> Vec<String> {

View file

@ -8,19 +8,20 @@ use itertools::Itertools;
use rustc_ast::ast::{self, UseTreeKind};
use rustc_span::{
BytePos, DUMMY_SP, Span,
symbol::{self, sym},
BytePos, Span, DUMMY_SP,
};
use crate::comment::combine_strs_with_missing_comments;
use crate::config::lists::*;
use crate::config::ImportGranularity;
use crate::config::{Edition, IndentStyle, Version};
use crate::config::lists::*;
use crate::config::{Edition, IndentStyle, StyleEdition};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::sort::version_sort;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::utils::{is_same_visibility, mk_sp, rewrite_ident};
@ -44,7 +45,8 @@ impl<'a> FmtVisitor<'a> {
Some(item.span.lo()),
Some(item.attrs.clone()),
)
.rewrite_top_level(&self.get_context(), shape);
.rewrite_top_level(&self.get_context(), shape)
.ok();
match rw {
Some(ref s) if s.is_empty() => {
// Format up to last newline
@ -104,7 +106,7 @@ pub(crate) enum UseSegmentKind {
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct UseSegment {
pub(crate) kind: UseSegmentKind,
pub(crate) version: Version,
pub(crate) style_edition: StyleEdition,
}
#[derive(Clone)]
@ -149,7 +151,7 @@ impl UseSegment {
};
UseSegment {
kind,
version: self.version,
style_edition: self.style_edition,
}
}
@ -197,7 +199,7 @@ impl UseSegment {
Some(UseSegment {
kind,
version: context.config.version(),
style_edition: context.config.style_edition(),
})
}
@ -331,12 +333,17 @@ impl UseTree {
&self,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| {
crate::utils::format_visibility(context, vis)
});
let use_str = self
.rewrite(context, shape.offset_left(vis.len())?)
.rewrite_result(
context,
shape
.offset_left(vis.len())
.max_width_error(shape.width, self.span())?,
)
.map(|s| {
if s.is_empty() {
s
@ -346,8 +353,8 @@ impl UseTree {
})?;
match self.attrs {
Some(ref attrs) if !attrs.is_empty() => {
let attr_str = attrs.rewrite(context, shape)?;
let lo = attrs.last().as_ref()?.span.hi();
let attr_str = attrs.rewrite_result(context, shape)?;
let lo = attrs.last().unknown_error()?.span.hi();
let hi = self.span.lo();
let span = mk_sp(lo, hi);
@ -368,7 +375,7 @@ impl UseTree {
allow_extend,
)
}
_ => Some(use_str),
_ => Ok(use_str),
}
}
@ -444,18 +451,21 @@ impl UseTree {
}
}
let version = context.config.version();
let style_edition = context.config.style_edition();
match a.kind {
UseTreeKind::Glob => {
// in case of a global path and the glob starts at the root, e.g., "::*"
if a.prefix.segments.len() == 1 && leading_modsep {
let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version });
result.path.push(UseSegment {
kind,
style_edition,
});
}
result.path.push(UseSegment {
kind: UseSegmentKind::Glob,
version,
style_edition,
});
}
UseTreeKind::Nested {
@ -470,7 +480,7 @@ impl UseTree {
",",
|tree| tree.span.lo(),
|tree| tree.span.hi(),
|_| Some("".to_owned()), // We only need comments for now.
|_| Ok("".to_owned()), // We only need comments for now.
context.snippet_provider.span_after(a.span, "{"),
a.span.hi(),
false,
@ -480,7 +490,10 @@ impl UseTree {
// e.g., "::{foo, bar}"
if a.prefix.segments.len() == 1 && leading_modsep {
let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version });
result.path.push(UseSegment {
kind,
style_edition,
});
}
let kind = UseSegmentKind::List(
list.iter()
@ -490,7 +503,10 @@ impl UseTree {
})
.collect(),
);
result.path.push(UseSegment { kind, version });
result.path.push(UseSegment {
kind,
style_edition,
});
}
UseTreeKind::Simple(ref rename) => {
// If the path has leading double colons and is composed of only 2 segments, then we
@ -519,7 +535,10 @@ impl UseTree {
_ => UseSegmentKind::Ident(name, alias),
};
let segment = UseSegment { kind, version };
let segment = UseSegment {
kind,
style_edition,
};
// `name` is already in result.
result.path.pop();
@ -614,7 +633,7 @@ impl UseTree {
list.sort();
last = UseSegment {
kind: UseSegmentKind::List(list),
version: last.version,
style_edition: last.style_edition,
};
}
@ -732,9 +751,12 @@ impl UseTree {
}) = self.path.last()
{
let self_segment = self.path.pop().unwrap();
let version = self_segment.version;
let style_edition = self_segment.style_edition;
let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]);
self.path.push(UseSegment { kind, version });
self.path.push(UseSegment {
kind,
style_edition,
});
}
self
}
@ -750,7 +772,7 @@ fn merge_rest(
return None;
}
if a.len() != len && b.len() != len {
let version = a[len].version;
let style_edition = a[len].style_edition;
if let UseSegmentKind::List(ref list) = a[len].kind {
let mut list = list.clone();
merge_use_trees_inner(
@ -760,7 +782,10 @@ fn merge_rest(
);
let mut new_path = b[..len].to_vec();
let kind = UseSegmentKind::List(list);
new_path.push(UseSegment { kind, version });
new_path.push(UseSegment {
kind,
style_edition,
});
return Some(new_path);
}
} else if len == 1 {
@ -770,9 +795,12 @@ fn merge_rest(
(&b[0], &a[1..])
};
let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string));
let version = a[0].version;
let style_edition = a[0].style_edition;
let mut list = vec![UseTree::from_path(
vec![UseSegment { kind, version }],
vec![UseSegment {
kind,
style_edition,
}],
DUMMY_SP,
)];
match rest {
@ -788,7 +816,7 @@ fn merge_rest(
b[0].clone(),
UseSegment {
kind: UseSegmentKind::List(list),
version,
style_edition,
},
]);
} else {
@ -801,8 +829,11 @@ fn merge_rest(
list.sort();
let mut new_path = b[..len].to_vec();
let kind = UseSegmentKind::List(list);
let version = a[0].version;
new_path.push(UseSegment { kind, version });
let style_edition = a[0].style_edition;
new_path.push(UseSegment {
kind,
style_edition,
});
Some(new_path)
}
@ -892,8 +923,8 @@ impl Ord for UseSegment {
| (Super(ref a), Super(ref b))
| (Crate(ref a), Crate(ref b)) => match (a, b) {
(Some(sa), Some(sb)) => {
if self.version == Version::Two {
sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#"))
if self.style_edition >= StyleEdition::Edition2024 {
version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#"))
} else {
a.cmp(b)
}
@ -902,25 +933,31 @@ impl Ord for UseSegment {
},
(Glob, Glob) => Ordering::Equal,
(Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => {
let (ia, ib) = if self.version == Version::Two {
let (ia, ib) = if self.style_edition >= StyleEdition::Edition2024 {
(pia.trim_start_matches("r#"), pib.trim_start_matches("r#"))
} else {
(pia.as_str(), pib.as_str())
};
// snake_case < CamelCase < UPPER_SNAKE_CASE
if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
return Ordering::Greater;
}
if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
return Ordering::Less;
}
if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
return Ordering::Greater;
}
if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
return Ordering::Less;
}
let ident_ord = ia.cmp(ib);
let ident_ord = if self.style_edition >= StyleEdition::Edition2024 {
version_sort(ia, ib)
} else {
// snake_case < CamelCase < UPPER_SNAKE_CASE
if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
return Ordering::Greater;
}
if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
return Ordering::Less;
}
if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
return Ordering::Greater;
}
if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
return Ordering::Less;
}
ia.cmp(ib)
};
if ident_ord != Ordering::Equal {
return ident_ord;
}
@ -928,9 +965,8 @@ impl Ord for UseSegment {
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(Some(aas), Some(abs)) => {
if self.version == Version::Two {
aas.trim_start_matches("r#")
.cmp(abs.trim_start_matches("r#"))
if self.style_edition >= StyleEdition::Edition2024 {
version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#"))
} else {
aas.cmp(abs)
}
@ -982,21 +1018,24 @@ fn rewrite_nested_use_tree(
context: &RewriteContext<'_>,
use_tree_list: &[UseTree],
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let mut list_items = Vec::with_capacity(use_tree_list.len());
let nested_shape = match context.config.imports_indent() {
IndentStyle::Block => shape
.block_indent(context.config.tab_spaces())
.with_max_width(context.config)
.sub_width(1)?,
.sub_width(1)
.unknown_error()?,
IndentStyle::Visual => shape.visual_indent(0),
};
for use_tree in use_tree_list {
if let Some(mut list_item) = use_tree.list_item.clone() {
list_item.item = use_tree.rewrite(context, nested_shape);
list_item.item = use_tree.rewrite_result(context, nested_shape);
list_items.push(list_item);
} else {
list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?));
list_items.push(ListItem::from_str(
use_tree.rewrite_result(context, nested_shape)?,
));
}
}
let has_nested_list = use_tree_list.iter().any(|use_segment| {
@ -1049,12 +1088,16 @@ fn rewrite_nested_use_tree(
format!("{{{list_str}}}")
};
Some(result)
Ok(result)
}
impl Rewrite for UseSegment {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(match self.kind {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
Ok(match self.kind {
UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
format!("{ident} as {rename}")
}
@ -1066,31 +1109,42 @@ impl Rewrite for UseSegment {
UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"),
UseSegmentKind::Crate(None) => "crate".to_owned(),
UseSegmentKind::Glob => "*".to_owned(),
UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree(
context,
use_tree_list,
// 1 = "{" and "}"
shape.offset_left(1)?.sub_width(1)?,
)?,
UseSegmentKind::List(ref use_tree_list) => {
rewrite_nested_use_tree(
context,
use_tree_list,
// 1 = "{" and "}"
shape
.offset_left(1)
.and_then(|s| s.sub_width(1))
.unknown_error()?,
)?
}
})
}
}
impl Rewrite for UseTree {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
// This does NOT format attributes and visibility or add a trailing `;`.
fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> {
fn rewrite_result(&self, context: &RewriteContext<'_>, mut shape: Shape) -> RewriteResult {
let mut result = String::with_capacity(256);
let mut iter = self.path.iter().peekable();
while let Some(segment) = iter.next() {
let segment_str = segment.rewrite(context, shape)?;
let segment_str = segment.rewrite_result(context, shape)?;
result.push_str(&segment_str);
if iter.peek().is_some() {
result.push_str("::");
// 2 = "::"
shape = shape.offset_left(2 + segment_str.len())?;
shape = shape
.offset_left(2 + segment_str.len())
.max_width_error(shape.width, self.span())?;
}
}
Some(result)
Ok(result)
}
}
@ -1114,7 +1168,7 @@ mod test {
struct Parser<'a> {
input: Peekable<Chars<'a>>,
version: Version,
style_edition: StyleEdition,
}
impl<'a> Parser<'a> {
@ -1132,7 +1186,7 @@ mod test {
buf: &mut String,
alias_buf: &mut Option<String>,
) {
let version = self.version;
let style_edition = self.style_edition;
if !buf.is_empty() {
let mut alias = None;
swap(alias_buf, &mut alias);
@ -1140,19 +1194,28 @@ mod test {
match buf.as_ref() {
"self" => {
let kind = UseSegmentKind::Slf(alias);
result.push(UseSegment { kind, version });
result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new();
*alias_buf = None;
}
"super" => {
let kind = UseSegmentKind::Super(alias);
result.push(UseSegment { kind, version });
result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new();
*alias_buf = None;
}
"crate" => {
let kind = UseSegmentKind::Crate(alias);
result.push(UseSegment { kind, version });
result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new();
*alias_buf = None;
}
@ -1160,7 +1223,10 @@ mod test {
let mut name = String::new();
swap(buf, &mut name);
let kind = UseSegmentKind::Ident(name, alias);
result.push(UseSegment { kind, version });
result.push(UseSegment {
kind,
style_edition,
});
}
}
}
@ -1178,7 +1244,7 @@ mod test {
let kind = UseSegmentKind::List(self.parse_list());
result.push(UseSegment {
kind,
version: self.version,
style_edition: self.style_edition,
});
self.eat('}');
}
@ -1188,7 +1254,7 @@ mod test {
let kind = UseSegmentKind::Glob;
result.push(UseSegment {
kind,
version: self.version,
style_edition: self.style_edition,
});
}
':' => {
@ -1249,7 +1315,7 @@ mod test {
let mut parser = Parser {
input: s.chars().peekable(),
version: Version::One,
style_edition: StyleEdition::Edition2015,
};
parser.parse_in_list()
}

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@
#![warn(unreachable_pub)]
#![recursion_limit = "256"]
#![allow(clippy::match_like_matches_macro)]
#![allow(unreachable_pub)]
// #[macro_use]
// extern crate tracing;
@ -48,8 +47,8 @@ use crate::shape::Indent;
use crate::utils::indent_next_line;
pub use crate::config::{
load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle,
Range, Verbosity,
CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range,
StyleEdition, Verbosity, Version, load_config,
};
pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder};
@ -94,6 +93,7 @@ mod rewrite;
pub(crate) mod rustfmt_diff;
mod shape;
mod skip;
mod sort;
pub(crate) mod source_file;
pub(crate) mod source_map;
mod spanned;

View file

@ -5,10 +5,10 @@ use std::iter::Peekable;
use rustc_span::BytePos;
use crate::comment::{find_comment_end, rewrite_comment, FindUncommented};
use crate::comment::{FindUncommented, find_comment_end, rewrite_comment};
use crate::config::lists::*;
use crate::config::{Config, IndentStyle};
use crate::rewrite::RewriteContext;
use crate::rewrite::{RewriteContext, RewriteError, RewriteResult};
use crate::shape::{Indent, Shape};
use crate::utils::{
count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline,
@ -125,18 +125,18 @@ pub(crate) struct ListItem {
pub(crate) pre_comment_style: ListItemCommentStyle,
// Item should include attributes and doc comments. None indicates a failed
// rewrite.
pub(crate) item: Option<String>,
pub(crate) item: RewriteResult,
pub(crate) post_comment: Option<String>,
// Whether there is extra whitespace before this item.
pub(crate) new_lines: bool,
}
impl ListItem {
pub(crate) fn empty() -> ListItem {
pub(crate) fn from_item(item: RewriteResult) -> ListItem {
ListItem {
pre_comment: None,
pre_comment_style: ListItemCommentStyle::None,
item: None,
item: item,
post_comment: None,
new_lines: false,
}
@ -185,7 +185,7 @@ impl ListItem {
ListItem {
pre_comment: None,
pre_comment_style: ListItemCommentStyle::None,
item: Some(s.into()),
item: Ok(s.into()),
post_comment: None,
new_lines: false,
}
@ -197,7 +197,11 @@ impl ListItem {
!matches!(*s, Some(ref s) if !s.is_empty())
}
!(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment))
fn empty_result(s: &RewriteResult) -> bool {
!matches!(*s, Ok(ref s) if !s.is_empty())
}
!(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment))
}
}
@ -257,7 +261,7 @@ where
}
// Format a list of commented items into a string.
pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> RewriteResult
where
I: IntoIterator<Item = T> + Clone,
T: AsRef<ListItem>,
@ -281,7 +285,7 @@ where
let indent_str = &formatting.shape.indent.to_string(formatting.config);
while let Some((i, item)) = iter.next() {
let item = item.as_ref();
let inner_item = item.item.as_ref()?;
let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?;
let first = i == 0;
let last = iter.peek().is_none();
let mut separate = match sep_place {
@ -516,7 +520,7 @@ where
prev_item_is_nested_import = inner_item.contains("::");
}
Some(result)
Ok(result)
}
fn max_width_of_item_with_post_comment<I, T>(
@ -741,7 +745,7 @@ where
I: Iterator<Item = T>,
F1: Fn(&T) -> BytePos,
F2: Fn(&T) -> BytePos,
F3: Fn(&T) -> Option<String>,
F3: Fn(&T) -> RewriteResult,
{
type Item = ListItem;
@ -775,8 +779,9 @@ where
ListItem {
pre_comment,
pre_comment_style,
// leave_last is set to true only for rewrite_items
item: if self.inner.peek().is_none() && self.leave_last {
None
Err(RewriteError::SkipFormatting)
} else {
(self.get_item_string)(&item)
},
@ -805,7 +810,7 @@ where
I: Iterator<Item = T>,
F1: Fn(&T) -> BytePos,
F2: Fn(&T) -> BytePos,
F3: Fn(&T) -> Option<String>,
F3: Fn(&T) -> RewriteResult,
{
ListItems {
snippet_provider,

View file

@ -10,35 +10,37 @@
// and those with brackets will be formatted as array literals.
use std::collections::HashMap;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::panic::{AssertUnwindSafe, catch_unwind};
use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
use rustc_ast::{ast, ptr};
use rustc_ast_pretty::pprust;
use rustc_span::{
BytePos, DUMMY_SP, Span, Symbol,
symbol::{self, kw},
BytePos, Span, Symbol, DUMMY_SP,
};
use tracing::debug;
use crate::comment::{
contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses,
CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment,
};
use crate::config::StyleEdition;
use crate::config::lists::*;
use crate::config::Version;
use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind};
use crate::lists::{itemize_list, write_list, ListFormatting};
use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs};
use crate::lists::{ListFormatting, itemize_list, write_list};
use crate::overflow;
use crate::parse::macros::lazy_static::parse_lazy_static;
use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args};
use crate::rewrite::{
MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult,
};
use crate::shape::{Indent, Shape};
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::utils::{
filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt,
NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout,
};
use crate::visitor::FmtVisitor;
@ -72,22 +74,30 @@ impl MacroArg {
impl Rewrite for ast::Item {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let mut visitor = crate::visitor::FmtVisitor::from_context(context);
visitor.block_indent = shape.indent;
visitor.last_pos = self.span().lo();
visitor.visit_item(self);
Some(visitor.buffer.to_owned())
Ok(visitor.buffer.to_owned())
}
}
impl Rewrite for MacroArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self {
MacroArg::Expr(ref expr) => expr.rewrite(context, shape),
MacroArg::Ty(ref ty) => ty.rewrite(context, shape),
MacroArg::Pat(ref pat) => pat.rewrite(context, shape),
MacroArg::Item(ref item) => item.rewrite(context, shape),
MacroArg::Keyword(ident, _) => Some(ident.name.to_string()),
MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape),
MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape),
MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape),
MacroArg::Item(ref item) => item.rewrite_result(context, shape),
MacroArg::Keyword(ident, _) => Ok(ident.name.to_string()),
}
}
}
@ -111,12 +121,14 @@ fn rewrite_macro_name(
}
// Use this on failing to format the macro call.
// TODO(ding-young) We should also report macro parse failure to tell users why given snippet
// is left unformatted. One possible improvement is appending formatting error to context.report
fn return_macro_parse_failure_fallback(
context: &RewriteContext<'_>,
indent: Indent,
position: MacroPosition,
span: Span,
) -> Option<String> {
) -> RewriteResult {
// Mark this as a failure however we format it
context.macro_rewrite_failure.replace(true);
@ -134,7 +146,8 @@ fn return_macro_parse_failure_fallback(
})
.unwrap_or(false);
if is_like_block_indent_style {
return trim_left_preserve_layout(context.snippet(span), indent, context.config);
return trim_left_preserve_layout(context.snippet(span), indent, context.config)
.macro_error(MacroErrorKind::Unknown, span);
}
context.skipped_range.borrow_mut().push((
@ -147,7 +160,7 @@ fn return_macro_parse_failure_fallback(
if position == MacroPosition::Item {
snippet.push(';');
}
Some(snippet)
Ok(snippet)
}
pub(crate) fn rewrite_macro(
@ -156,13 +169,13 @@ pub(crate) fn rewrite_macro(
context: &RewriteContext<'_>,
shape: Shape,
position: MacroPosition,
) -> Option<String> {
) -> RewriteResult {
let should_skip = context
.skip_context
.macros
.skip(context.snippet(mac.path.span));
if should_skip {
None
Err(RewriteError::SkipFormatting)
} else {
let guard = context.enter_macro();
let result = catch_unwind(AssertUnwindSafe(|| {
@ -176,9 +189,16 @@ pub(crate) fn rewrite_macro(
)
}));
match result {
Err(..) | Ok(None) => {
Err(..) => {
context.macro_rewrite_failure.replace(true);
None
Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: mac.span(),
})
}
Ok(Err(e)) => {
context.macro_rewrite_failure.replace(true);
Err(e)
}
Ok(rw) => rw,
}
@ -192,11 +212,11 @@ fn rewrite_macro_inner(
shape: Shape,
position: MacroPosition,
is_nested_macro: bool,
) -> Option<String> {
) -> RewriteResult {
if context.config.use_try_shorthand() {
if let Some(expr) = convert_try_mac(mac, context) {
context.leave_macro();
return expr.rewrite(context, shape);
return expr.rewrite_result(context, shape);
}
}
@ -216,21 +236,27 @@ fn rewrite_macro_inner(
if ts.is_empty() && !has_comment {
return match style {
Delimiter::Parenthesis if position == MacroPosition::Item => {
Some(format!("{macro_name}();"))
Ok(format!("{macro_name}();"))
}
Delimiter::Bracket if position == MacroPosition::Item => {
Some(format!("{macro_name}[];"))
}
Delimiter::Parenthesis => Some(format!("{macro_name}()")),
Delimiter::Bracket => Some(format!("{macro_name}[]")),
Delimiter::Brace => Some(format!("{macro_name} {{}}")),
Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")),
Delimiter::Parenthesis => Ok(format!("{macro_name}()")),
Delimiter::Bracket => Ok(format!("{macro_name}[]")),
Delimiter::Brace => Ok(format!("{macro_name} {{}}")),
_ => unreachable!(),
};
}
// Format well-known macros which cannot be parsed as a valid AST.
if macro_name == "lazy_static!" && !has_comment {
if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) {
return success;
match format_lazy_static(context, shape, ts.clone(), mac.span()) {
Ok(rw) => return Ok(rw),
Err(err) => match err {
// We will move on to parsing macro args just like other macros
// if we could not parse lazy_static! with known syntax
RewriteError::MacroFailure { kind, span: _ }
if kind == MacroErrorKind::ParseFailure => {}
// If formatting fails even though parsing succeeds, return the err early
_ => return Err(err),
},
}
}
@ -267,7 +293,7 @@ fn rewrite_macro_inner(
Delimiter::Parenthesis => {
// Handle special case: `vec!(expr; expr)`
if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style)
handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
} else {
// Format macro invocation as function call, preserve the trailing
// comma because not all macros support them.
@ -293,7 +319,7 @@ fn rewrite_macro_inner(
Delimiter::Bracket => {
// Handle special case: `vec![expr; expr]`
if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style)
handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
} else {
// If we are rewriting `vec!` macro or other special macros,
// then we can rewrite this as a usual array literal.
@ -323,7 +349,7 @@ fn rewrite_macro_inner(
_ => "",
};
Some(format!("{rewrite}{comma}"))
Ok(format!("{rewrite}{comma}"))
}
}
Delimiter::Brace => {
@ -332,8 +358,8 @@ fn rewrite_macro_inner(
// anything in between the braces (for now).
let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{');
match trim_left_preserve_layout(snippet, shape.indent, context.config) {
Some(macro_body) => Some(format!("{macro_name} {macro_body}")),
None => Some(format!("{macro_name} {snippet}")),
Some(macro_body) => Ok(format!("{macro_name} {macro_body}")),
None => Ok(format!("{macro_name} {snippet}")),
}
}
_ => unreachable!(),
@ -346,28 +372,32 @@ fn handle_vec_semi(
arg_vec: Vec<MacroArg>,
macro_name: String,
delim_token: Delimiter,
) -> Option<String> {
span: Span,
) -> RewriteResult {
let (left, right) = match delim_token {
Delimiter::Parenthesis => ("(", ")"),
Delimiter::Bracket => ("[", "]"),
_ => unreachable!(),
};
let mac_shape = shape.offset_left(macro_name.len())?;
// Should we return MaxWidthError, Or Macro failure
let mac_shape = shape
.offset_left(macro_name.len())
.max_width_error(shape.width, span)?;
// 8 = `vec![]` + `; ` or `vec!()` + `; `
let total_overhead = 8;
let nested_shape = mac_shape.block_indent(context.config.tab_spaces());
let lhs = arg_vec[0].rewrite(context, nested_shape)?;
let rhs = arg_vec[1].rewrite(context, nested_shape)?;
let lhs = arg_vec[0].rewrite_result(context, nested_shape)?;
let rhs = arg_vec[1].rewrite_result(context, nested_shape)?;
if !lhs.contains('\n')
&& !rhs.contains('\n')
&& lhs.len() + rhs.len() + total_overhead <= shape.width
{
// macro_name(lhs; rhs) or macro_name[lhs; rhs]
Some(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
} else {
// macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n]
Some(format!(
Ok(format!(
"{}{}{}{};{}{}{}{}",
macro_name,
left,
@ -385,7 +415,7 @@ fn rewrite_empty_macro_def_body(
context: &RewriteContext<'_>,
span: Span,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
// Create an empty, dummy `ast::Block` representing an empty macro body
let block = ast::Block {
stmts: vec![].into(),
@ -395,7 +425,7 @@ fn rewrite_empty_macro_def_body(
tokens: None,
could_be_bare_literal: false,
};
block.rewrite(context, shape)
block.rewrite_result(context, shape)
}
pub(crate) fn rewrite_macro_def(
@ -406,8 +436,8 @@ pub(crate) fn rewrite_macro_def(
ident: symbol::Ident,
vis: &ast::Visibility,
span: Span,
) -> Option<String> {
let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
) -> RewriteResult {
let snippet = Ok(remove_trailing_white_spaces(context.snippet(span)));
if snippet.as_ref().map_or(true, |s| s.ends_with(';')) {
return snippet;
}
@ -442,7 +472,7 @@ pub(crate) fn rewrite_macro_def(
let lo = context.snippet_provider.span_before(span, "{");
result += " ";
result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?;
return Some(result);
return Ok(result);
}
let branch_items = itemize_list(
@ -453,13 +483,14 @@ pub(crate) fn rewrite_macro_def(
|branch| branch.span.lo(),
|branch| branch.span.hi(),
|branch| match branch.rewrite(context, arm_shape, multi_branch_style) {
Some(v) => Some(v),
Ok(v) => Ok(v),
// if the rewrite returned None because a macro could not be rewritten, then return the
// original body
None if context.macro_rewrite_failure.get() => {
Some(context.snippet(branch.body).trim().to_string())
// TODO(ding-young) report rewrite error even if we return Ok with original snippet
Err(_) if context.macro_rewrite_failure.get() => {
Ok(context.snippet(branch.body).trim().to_string())
}
None => None,
Err(e) => Err(e),
},
context.snippet_provider.span_after(span, "{"),
span.hi(),
@ -478,8 +509,8 @@ pub(crate) fn rewrite_macro_def(
}
match write_list(&branch_items, &fmt) {
Some(ref s) => result += s,
None => return snippet,
Ok(ref s) => result += s,
Err(_) => return snippet,
}
if multi_branch_style {
@ -487,7 +518,7 @@ pub(crate) fn rewrite_macro_def(
result += "}";
}
Some(result)
Ok(result)
}
fn register_metavariable(
@ -639,12 +670,13 @@ impl MacroArgKind {
context: &RewriteContext<'_>,
shape: Shape,
use_multiple_lines: bool,
) -> Option<String> {
let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> {
) -> RewriteResult {
type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>;
let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite {
let inner = wrap_macro_args(context, args, shape)?;
let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty());
if lhs.len() + inner.len() + rhs.len() <= shape.width {
return Some((lhs, inner, rhs));
return Ok((lhs, inner, rhs));
}
let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false);
@ -652,27 +684,27 @@ impl MacroArgKind {
.block_indent(context.config.tab_spaces())
.with_max_width(context.config);
let inner = wrap_macro_args(context, args, nested_shape)?;
Some((lhs, inner, rhs))
Ok((lhs, inner, rhs))
};
match *self {
MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")),
MacroArgKind::MetaVariable(ty, ref name) => Ok(format!("${name}:{ty}")),
MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => {
let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?;
let another = another
.as_ref()
.and_then(|a| a.rewrite(context, shape, use_multiple_lines))
.and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok())
.unwrap_or_else(|| "".to_owned());
let repeat_tok = pprust::token_to_string(tok);
Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
Ok(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
}
MacroArgKind::Delimited(delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")),
MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")),
MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")),
MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")),
}
}
}
@ -688,7 +720,7 @@ impl ParsedMacroArg {
context: &RewriteContext<'_>,
shape: Shape,
use_multiple_lines: bool,
) -> Option<String> {
) -> RewriteResult {
self.kind.rewrite(context, shape, use_multiple_lines)
}
}
@ -966,9 +998,9 @@ fn wrap_macro_args(
context: &RewriteContext<'_>,
args: &[ParsedMacroArg],
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
wrap_macro_args_inner(context, args, shape, false)
.or_else(|| wrap_macro_args_inner(context, args, shape, true))
.or_else(|_| wrap_macro_args_inner(context, args, shape, true))
}
fn wrap_macro_args_inner(
@ -976,7 +1008,7 @@ fn wrap_macro_args_inner(
args: &[ParsedMacroArg],
shape: Shape,
use_multiple_lines: bool,
) -> Option<String> {
) -> RewriteResult {
let mut result = String::with_capacity(128);
let mut iter = args.iter().peekable();
let indent_str = shape.indent.to_string_with_newline(context.config);
@ -1002,9 +1034,9 @@ fn wrap_macro_args_inner(
}
if !use_multiple_lines && result.len() >= shape.width {
None
Err(RewriteError::Unknown)
} else {
Some(result)
Ok(result)
}
}
@ -1012,22 +1044,21 @@ fn wrap_macro_args_inner(
// for some common cases. I hope the basic logic is sufficient. Note that the
// meaning of some tokens is a bit different here from usual Rust, e.g., `*`
// and `(`/`)` have special meaning.
//
// We always try and format on one line.
// FIXME: Use multi-line when every thing does not fit on one line.
fn format_macro_args(
context: &RewriteContext<'_>,
token_stream: TokenStream,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let span = span_for_token_stream(&token_stream);
if !context.config.format_macro_matchers() {
let span = span_for_token_stream(&token_stream);
return Some(match span {
return Ok(match span {
Some(span) => context.snippet(span).to_owned(),
None => String::new(),
});
}
let parsed_args = MacroArgParser::new().parse(token_stream)?;
let parsed_args = MacroArgParser::new()
.parse(token_stream)
.macro_error(MacroErrorKind::ParseFailure, span.unwrap())?;
wrap_macro_args(context, &parsed_args, shape)
}
@ -1242,23 +1273,31 @@ impl MacroBranch {
context: &RewriteContext<'_>,
shape: Shape,
multi_branch_style: bool,
) -> Option<String> {
) -> RewriteResult {
// Only attempt to format function-like macros.
if self.args_paren_kind != Delimiter::Parenthesis {
// FIXME(#1539): implement for non-sugared macros.
return None;
return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: self.span,
});
}
let old_body = context.snippet(self.body).trim();
let has_block_body = old_body.starts_with('{');
let mut prefix_width = 5; // 5 = " => {"
if context.config.version() == Version::Two {
if context.config.style_edition() >= StyleEdition::Edition2024 {
if has_block_body {
prefix_width = 6; // 6 = " => {{"
}
}
let mut result =
format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?;
let mut result = format_macro_args(
context,
self.args.clone(),
shape
.sub_width(prefix_width)
.max_width_error(shape.width, self.span)?,
)?;
if multi_branch_style {
result += " =>";
@ -1267,7 +1306,7 @@ impl MacroBranch {
if !context.config.format_macro_bodies() {
result += " ";
result += context.snippet(self.whole_body);
return Some(result);
return Ok(result);
}
// The macro body is the most interesting part. It might end up as various
@ -1276,7 +1315,8 @@ impl MacroBranch {
// `$$`). We'll try and format like an AST node, but we'll substitute
// variables for new names with the same length first.
let (body_str, substs) = replace_names(old_body)?;
let (body_str, substs) =
replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?;
let mut config = context.config.clone();
config.set().show_parse_errors(false);
@ -1299,13 +1339,21 @@ impl MacroBranch {
config.set().max_width(new_width);
match crate::format_code_block(&body_str, &config, true) {
Some(new_body) => new_body,
None => return None,
None => {
return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: self.span,
});
}
}
}
};
if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
return None;
return Err(RewriteError::ExceedsMaxWidth {
configured_width: shape.width,
span: self.span,
});
}
// Indent the body since it is in a block.
@ -1331,7 +1379,10 @@ impl MacroBranch {
for (old, new) in &substs {
if old_body.contains(new) {
debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
return None;
return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::ReplaceMacroVariable,
span: self.span,
});
}
new_body = new_body.replace(new, old);
}
@ -1346,7 +1397,7 @@ impl MacroBranch {
result += "}";
Some(result)
Ok(result)
}
}
@ -1366,7 +1417,8 @@ fn format_lazy_static(
context: &RewriteContext<'_>,
shape: Shape,
ts: TokenStream,
) -> Option<String> {
span: Span,
) -> RewriteResult {
let mut result = String::with_capacity(1024);
let nested_shape = shape
.block_indent(context.config.tab_spaces())
@ -1375,7 +1427,8 @@ fn format_lazy_static(
result.push_str("lazy_static! {");
result.push_str(&nested_shape.indent.to_string_with_newline(context.config));
let parsed_elems = parse_lazy_static(context, ts)?;
let parsed_elems =
parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?;
let last = parsed_elems.len() - 1;
for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() {
// Rewrite as a static item.
@ -1385,14 +1438,16 @@ fn format_lazy_static(
"{}static ref {}: {} =",
vis,
id,
ty.rewrite(context, nested_shape)?
ty.rewrite_result(context, nested_shape)?
));
result.push_str(&rewrite_assign_rhs(
context,
stmt,
&*expr,
&RhsAssignKind::Expr(&expr.kind, expr.span),
nested_shape.sub_width(1)?,
nested_shape
.sub_width(1)
.max_width_error(nested_shape.width, expr.span)?,
)?);
result.push(';');
if i != last {
@ -1403,7 +1458,7 @@ fn format_lazy_static(
result.push_str(&shape.indent.to_string_with_newline(context.config));
result.push('}');
Some(result)
Ok(result)
}
fn rewrite_macro_with_items(
@ -1415,12 +1470,12 @@ fn rewrite_macro_with_items(
original_style: Delimiter,
position: MacroPosition,
span: Span,
) -> Option<String> {
) -> RewriteResult {
let style_to_delims = |style| match style {
Delimiter::Parenthesis => Some(("(", ")")),
Delimiter::Bracket => Some(("[", "]")),
Delimiter::Brace => Some((" {", "}")),
_ => None,
Delimiter::Parenthesis => Ok(("(", ")")),
Delimiter::Bracket => Ok(("[", "]")),
Delimiter::Brace => Ok((" {", "}")),
_ => Err(RewriteError::Unknown),
};
let (opener, closer) = style_to_delims(style)?;
@ -1442,7 +1497,7 @@ fn rewrite_macro_with_items(
for item in items {
let item = match item {
MacroArg::Item(item) => item,
_ => return None,
_ => return Err(RewriteError::Unknown),
};
visitor.visit_item(item);
}
@ -1455,5 +1510,5 @@ fn rewrite_macro_with_items(
result.push_str(&shape.indent.to_string_with_newline(context.config));
result.push_str(closer);
result.push_str(trailing_semicolon);
Some(result)
Ok(result)
}

View file

@ -2,19 +2,19 @@
use std::iter::repeat;
use rustc_ast::{ast, ptr, MatchKind};
use rustc_ast::{MatchKind, ast, ptr};
use rustc_span::{BytePos, Span};
use tracing::debug;
use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented};
use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment};
use crate::config::lists::*;
use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version};
use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition};
use crate::expr::{
format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond,
ExprType, RhsTactics,
ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block,
prefer_next_line, rewrite_cond,
};
use crate::lists::{itemize_list, write_list, ListFormatting};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::lists::{ListFormatting, itemize_list, write_list};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
@ -56,6 +56,10 @@ impl<'a> Spanned for ArmWrapper<'a> {
impl<'a> Rewrite for ArmWrapper<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
rewrite_match_arm(
context,
self.arm,
@ -74,7 +78,7 @@ pub(crate) fn rewrite_match(
span: Span,
attrs: &[ast::Attribute],
match_kind: MatchKind,
) -> Option<String> {
) -> RewriteResult {
// Do not take the rhs overhead from the upper expressions into account
// when rewriting match condition.
let cond_shape = Shape {
@ -83,10 +87,14 @@ pub(crate) fn rewrite_match(
};
// 6 = `match `
let cond_shape = match context.config.indent_style() {
IndentStyle::Visual => cond_shape.shrink_left(6)?,
IndentStyle::Block => cond_shape.offset_left(6)?,
IndentStyle::Visual => cond_shape
.shrink_left(6)
.max_width_error(shape.width, span)?,
IndentStyle::Block => cond_shape
.offset_left(6)
.max_width_error(shape.width, span)?,
};
let cond_str = cond.rewrite(context, cond_shape)?;
let cond_str = cond.rewrite_result(context, cond_shape)?;
let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
let block_sep = match context.config.control_brace_style() {
ControlBraceStyle::AlwaysNextLine => alt_block_sep,
@ -105,12 +113,13 @@ pub(crate) fn rewrite_match(
let inner_attrs_str = if inner_attrs.is_empty() {
String::new()
} else {
let shape = match context.config.version() {
Version::One => shape,
_ => shape.block_indent(context.config.tab_spaces()),
let shape = if context.config.style_edition() <= StyleEdition::Edition2021 {
shape
} else {
shape.block_indent(context.config.tab_spaces())
};
inner_attrs
.rewrite(context, shape)
.rewrite_result(context, shape)
.map(|s| format!("{}{}\n", nested_indent_str, s))?
};
@ -130,16 +139,16 @@ pub(crate) fn rewrite_match(
if arms.is_empty() {
let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1)));
if snippet.trim().is_empty() {
Some(format!("match {cond_str} {{}}"))
Ok(format!("match {cond_str} {{}}"))
} else {
// Empty match with comments or inner attributes? We are not going to bother, sorry ;)
Some(context.snippet(span).to_owned())
Ok(context.snippet(span).to_owned())
}
} else {
let span_after_cond = mk_sp(cond.span.hi(), span.hi());
match match_kind {
MatchKind::Prefix => Some(format!(
MatchKind::Prefix => Ok(format!(
"match {}{}{{\n{}{}{}\n{}}}",
cond_str,
block_sep,
@ -148,7 +157,7 @@ pub(crate) fn rewrite_match(
rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
shape.indent.to_string(context.config),
)),
MatchKind::Postfix => Some(format!(
MatchKind::Postfix => Ok(format!(
"{}.match{}{{\n{}{}{}\n{}}}",
cond_str,
block_sep,
@ -198,7 +207,7 @@ fn rewrite_match_arms(
shape: Shape,
span: Span,
open_brace_pos: BytePos,
) -> Option<String> {
) -> RewriteResult {
let arm_shape = shape
.block_indent(context.config.tab_spaces())
.with_max_width(context.config);
@ -218,7 +227,7 @@ fn rewrite_match_arms(
"|",
|arm| arm.span().lo(),
|arm| arm.span().hi(),
|arm| arm.rewrite(context, arm_shape),
|arm| arm.rewrite_result(context, arm_shape),
open_brace_pos,
span.hi(),
false,
@ -238,19 +247,19 @@ fn rewrite_match_arm(
shape: Shape,
is_last: bool,
has_leading_pipe: bool,
) -> Option<String> {
) -> RewriteResult {
let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
if contains_skip(&arm.attrs) {
let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None);
let (_, body) = flatten_arm_body(context, arm.body.as_deref().unknown_error()?, None);
// `arm.span()` does not include trailing comma, add it manually.
return Some(format!(
return Ok(format!(
"{}{}",
context.snippet(arm.span()),
arm_comma(context.config, body, is_last),
));
}
let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo());
(missing_span, arm.attrs.rewrite(context, shape)?)
(missing_span, arm.attrs.rewrite_result(context, shape)?)
} else {
(mk_sp(arm.span().lo(), arm.span().lo()), String::new())
};
@ -264,19 +273,25 @@ fn rewrite_match_arm(
};
// Patterns
let pat_shape = match &arm.body.as_ref()?.kind {
let pat_shape = match &arm.body.as_ref().unknown_error()?.kind {
ast::ExprKind::Block(_, Some(label)) => {
// Some block with a label ` => 'label: {`
// 7 = ` => : {`
let label_len = label.ident.as_str().len();
shape.sub_width(7 + label_len)?.offset_left(pipe_offset)?
shape
.sub_width(7 + label_len)
.and_then(|s| s.offset_left(pipe_offset))
.max_width_error(shape.width, arm.span)?
}
_ => {
// 5 = ` => {`
shape.sub_width(5)?.offset_left(pipe_offset)?
shape
.sub_width(5)
.and_then(|s| s.offset_left(pipe_offset))
.max_width_error(shape.width, arm.span)?
}
};
let pats_str = arm.pat.rewrite(context, pat_shape)?;
let pats_str = arm.pat.rewrite_result(context, pat_shape)?;
// Guard
let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces();
@ -298,10 +313,13 @@ fn rewrite_match_arm(
false,
)?;
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo());
let arrow_span = mk_sp(
arm.pat.span.hi(),
arm.body.as_ref().unknown_error()?.span().lo(),
);
rewrite_match_body(
context,
arm.body.as_ref()?,
arm.body.as_ref().unknown_error()?,
&lhs_str,
shape,
guard_str.contains('\n'),
@ -382,7 +400,7 @@ fn rewrite_match_body(
has_guard: bool,
arrow_span: Span,
is_last: bool,
) -> Option<String> {
) -> RewriteResult {
let (extend, body) = flatten_arm_body(
context,
body,
@ -403,7 +421,7 @@ fn rewrite_match_body(
_ => " ",
};
Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
Ok(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
};
let next_line_indent = if !is_block || is_empty_block {
@ -420,7 +438,7 @@ fn rewrite_match_body(
let arrow_snippet = context.snippet(arrow_span).trim();
// search for the arrow starting from the end of the snippet since there may be a match
// expression within the guard
let arrow_index = if context.config.version() == Version::One {
let arrow_index = if context.config.style_edition() <= StyleEdition::Edition2021 {
arrow_snippet.rfind("=>").unwrap()
} else {
arrow_snippet.find_last_uncommented("=>").unwrap()
@ -447,7 +465,7 @@ fn rewrite_match_body(
result.push_str(&nested_indent_str);
result.push_str(body_str);
result.push_str(comma);
return Some(result);
return Ok(result);
}
let indent_str = shape.indent.to_string_with_newline(context.config);
@ -458,7 +476,7 @@ fn rewrite_match_body(
} else {
""
};
let semicolon = if context.config.version() == Version::One {
let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 {
""
} else {
if semicolon_for_expr(context, body) {
@ -490,7 +508,7 @@ fn rewrite_match_body(
result.push_str(&block_sep);
result.push_str(body_str);
result.push_str(&body_suffix);
Some(result)
Ok(result)
};
// Let's try and get the arm body on the same line as the condition.
@ -499,7 +517,7 @@ fn rewrite_match_body(
.offset_left(extra_offset(pats_str, shape) + 4)
.and_then(|shape| shape.sub_width(comma.len()));
let orig_body = if forbid_same_line || !arrow_comment.is_empty() {
None
Err(RewriteError::Unknown)
} else if let Some(body_shape) = orig_body_shape {
let rewrite = nop_block_collapse(
format_expr(body, ExprType::Statement, context, body_shape),
@ -507,7 +525,7 @@ fn rewrite_match_body(
);
match rewrite {
Some(ref body_str)
Ok(ref body_str)
if is_block
|| (!body_str.contains('\n')
&& unicode_str_width(body_str) <= body_shape.width) =>
@ -517,7 +535,7 @@ fn rewrite_match_body(
_ => rewrite,
}
} else {
None
Err(RewriteError::Unknown)
};
let orig_budget = orig_body_shape.map_or(0, |shape| shape.width);
@ -528,20 +546,23 @@ fn rewrite_match_body(
next_line_body_shape.width,
);
match (orig_body, next_line_body) {
(Some(ref orig_str), Some(ref next_line_str))
(Ok(ref orig_str), Ok(ref next_line_str))
if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) =>
{
combine_next_line_body(next_line_str)
}
(Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
(Ok(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
combine_orig_body(orig_str)
}
(Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => {
(Ok(ref orig_str), Ok(ref next_line_str)) if orig_str.contains('\n') => {
combine_next_line_body(next_line_str)
}
(None, Some(ref next_line_str)) => combine_next_line_body(next_line_str),
(None, None) => None,
(Some(ref orig_str), _) => combine_orig_body(orig_str),
(Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str),
// When both orig_body and next_line_body result in errors, we currently propagate the
// error from the second attempt since it is more generous with width constraints.
// This decision is somewhat arbitrary and is open to change.
(Err(_), Err(next_line_err)) => Err(next_line_err),
(Ok(ref orig_str), _) => combine_orig_body(orig_str),
}
}
@ -554,7 +575,7 @@ fn rewrite_guard(
// the arm (excludes offset).
pattern_width: usize,
multiline_pattern: bool,
) -> Option<String> {
) -> RewriteResult {
if let Some(ref guard) = *guard {
// First try to fit the guard string on the same line as the pattern.
// 4 = ` if `, 5 = ` => {`
@ -563,9 +584,9 @@ fn rewrite_guard(
.and_then(|s| s.sub_width(5));
if !multiline_pattern {
if let Some(cond_shape) = cond_shape {
if let Some(cond_str) = guard.rewrite(context, cond_shape) {
if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) {
if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() {
return Some(format!(" if {cond_str}"));
return Ok(format!(" if {cond_str}"));
}
}
}
@ -575,24 +596,20 @@ fn rewrite_guard(
// 3 = `if `, 5 = ` => {`
let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config)
.offset_left(3)
.and_then(|s| s.sub_width(5));
if let Some(cond_shape) = cond_shape {
if let Some(cond_str) = guard.rewrite(context, cond_shape) {
return Some(format!(
"{}if {}",
cond_shape.indent.to_string_with_newline(context.config),
cond_str
));
}
}
None
.and_then(|s| s.sub_width(5))
.max_width_error(shape.width, guard.span)?;
let cond_str = guard.rewrite_result(context, cond_shape)?;
Ok(format!(
"{}if {}",
cond_shape.indent.to_string_with_newline(context.config),
cond_str
))
} else {
Some(String::new())
Ok(String::new())
}
}
fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult {
debug!("nop_block_collapse {:?} {}", block_str, budget);
block_str.map(|block_str| {
if block_str.starts_with('{')

View file

@ -1,10 +1,10 @@
use rustc_span::{BytePos, Pos, Span};
use tracing::debug;
use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices};
use crate::config::file_lines::FileLines;
use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment};
use crate::config::FileName;
use crate::config::Version;
use crate::config::StyleEdition;
use crate::config::file_lines::FileLines;
use crate::coverage::transform_missing_snippet;
use crate::shape::{Indent, Shape};
use crate::source_map::LineRangeUtils;
@ -247,7 +247,9 @@ impl<'a> FmtVisitor<'a> {
let indent_str = self.block_indent.to_string(self.config);
self.push_str(&indent_str);
self.block_indent
} else if self.config.version() == Version::Two && !snippet.starts_with('\n') {
} else if self.config.style_edition() >= StyleEdition::Edition2024
&& !snippet.starts_with('\n')
{
// The comment appears on the same line as the previous formatted code.
// Assuming that comment is logically associated with that code, we want to keep it on
// the same level and avoid mixing it with possible other comment.
@ -283,13 +285,13 @@ impl<'a> FmtVisitor<'a> {
let other_lines = &subslice[offset + 1..];
let comment_str =
rewrite_comment(other_lines, false, comment_shape, self.config)
.unwrap_or_else(|| String::from(other_lines));
.unwrap_or_else(|_| String::from(other_lines));
self.push_str(&comment_str);
}
}
} else {
let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
.unwrap_or_else(|| String::from(subslice));
.unwrap_or_else(|_| String::from(subslice));
self.push_str(&comment_str);
}

View file

@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};
use rustc_ast::ast;
use rustc_ast::visit::Visitor;
use rustc_span::symbol::{self, sym, Symbol};
use rustc_span::Span;
use rustc_span::symbol::{self, Symbol, sym};
use thin_vec::ThinVec;
use thiserror::Error;

View file

@ -9,22 +9,22 @@ use rustc_span::Span;
use tracing::debug;
use crate::closures;
use crate::config::Version;
use crate::config::{lists::*, Config};
use crate::config::StyleEdition;
use crate::config::{Config, lists::*};
use crate::expr::{
can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
rewrite_cond,
};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
};
use crate::macros::MacroArg;
use crate::patterns::{can_be_overflowed_pat, TuplePatField};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::patterns::{TuplePatField, can_be_overflowed_pat};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::types::{can_be_overflowed_type, SegmentParam};
use crate::types::{SegmentParam, can_be_overflowed_type};
use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
/// A list of `format!`-like macros, that take a long format string and a list of arguments to
@ -91,6 +91,10 @@ impl<'a> Rewrite for OverflowableItem<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.map(|item| item.rewrite(context, shape))
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
self.map(|item| item.rewrite_result(context, shape))
}
}
impl<'a> Spanned for OverflowableItem<'a> {
@ -201,8 +205,12 @@ impl<'a> OverflowableItem<'a> {
OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR,
_ => &[],
};
let additional_cases = match (self, config.version()) {
(OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2,
let additional_cases = match self {
OverflowableItem::MacroArg(..)
if config.style_edition() >= StyleEdition::Edition2024 =>
{
SPECIAL_CASE_MACROS_V2
}
_ => &[],
};
base_cases.iter().chain(additional_cases)
@ -278,7 +286,7 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
span: Span,
item_max_width: usize,
force_separator_tactic: Option<SeparatorTactic>,
) -> Option<String> {
) -> RewriteResult {
Context::new(
context,
items,
@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
items: impl Iterator<Item = &'a T>,
shape: Shape,
span: Span,
) -> Option<String> {
) -> RewriteResult {
Context::new(
context,
items,
@ -324,7 +332,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>
span: Span,
force_separator_tactic: Option<SeparatorTactic>,
delim_token: Option<Delimiter>,
) -> Option<String> {
) -> RewriteResult {
let (lhs, rhs) = match delim_token {
Some(Delimiter::Parenthesis) => ("(", ")"),
Some(Delimiter::Brace) => ("{", "}"),
@ -428,7 +436,7 @@ impl<'a> Context<'a> {
if closures::args_have_many_closure(&self.items) {
None
} else {
closures::rewrite_last_closure(self.context, expr, shape)
closures::rewrite_last_closure(self.context, expr, shape).ok()
}
}
@ -457,7 +465,7 @@ impl<'a> Context<'a> {
if let Some(rewrite) = rewrite {
// splitn(2, *).next().unwrap() is always safe.
let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned());
last_list_item.item = rewrite_first_line;
Some(rewrite)
} else {
@ -495,7 +503,7 @@ impl<'a> Context<'a> {
Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
if !combine_arg_with_callee
&& is_method_call(expr)
&& self.context.config.version() == Version::Two =>
&& self.context.config.style_edition() >= StyleEdition::Edition2024 =>
{
self.context.force_one_line_chain.replace(true);
}
@ -545,22 +553,23 @@ impl<'a> Context<'a> {
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
if no_newline {
list_items[self.items.len() - 1].item = rw;
list_items[self.items.len() - 1].item = rw.unknown_error();
} else {
list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
}
} else {
list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
}
}
(true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
list_items[self.items.len() - 1].item = placeholder;
list_items[self.items.len() - 1].item = placeholder.unknown_error();
}
_ if !self.items.is_empty() => {
list_items[self.items.len() - 1].item = self
.items
.last()
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape))
.unknown_error();
// Use horizontal layout for a function with a single argument as long as
// everything fits in a single line.
@ -613,7 +622,7 @@ impl<'a> Context<'a> {
tactic
}
fn rewrite_items(&self) -> Option<(bool, String)> {
fn rewrite_items(&self) -> Result<(bool, String), RewriteError> {
let span = self.items_span();
debug!("items: {:?}", self.items);
@ -624,7 +633,7 @@ impl<'a> Context<'a> {
",",
|item| item.span().lo(),
|item| item.span().hi(),
|item| item.rewrite(self.context, self.nested_shape),
|item| item.rewrite_result(self.context, self.nested_shape),
span.lo(),
span.hi(),
true,
@ -689,7 +698,8 @@ impl<'a> Context<'a> {
);
result.push_str(self.ident);
result.push_str(prefix);
let force_single_line = if self.context.config.version() == Version::Two {
let force_single_line = if self.context.config.style_edition() >= StyleEdition::Edition2024
{
!self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
} else {
// 2 = `()`
@ -711,7 +721,7 @@ impl<'a> Context<'a> {
result
}
fn rewrite(&self, shape: Shape) -> Option<String> {
fn rewrite(&self, shape: Shape) -> RewriteResult {
let (extendable, items_str) = self.rewrite_items()?;
// If we are using visual indent style and failed to format, retry with block indent.
@ -725,7 +735,7 @@ impl<'a> Context<'a> {
return result;
}
Some(self.wrap_items(&items_str, shape, extendable))
Ok(self.wrap_items(&items_str, shape, extendable))
}
}

View file

@ -1,9 +1,11 @@
use rustc_ast::ast;
use rustc_span::Span;
use crate::config::lists::*;
use crate::config::IndentStyle;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::config::lists::*;
use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::spanned::Spanned;
use crate::utils::{
first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str,
};
@ -40,16 +42,19 @@ pub(crate) fn rewrite_all_pairs(
expr: &ast::Expr,
shape: Shape,
context: &RewriteContext<'_>,
) -> Option<String> {
expr.flatten(context, shape).and_then(|list| {
if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
rewrite_pairs_multiline(&list, shape, context)
} else {
// First we try formatting on one line.
rewrite_pairs_one_line(&list, shape, context)
.or_else(|| rewrite_pairs_multiline(&list, shape, context))
}
})
) -> RewriteResult {
expr.flatten(context, shape)
.unknown_error()
.and_then(|list| {
if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
rewrite_pairs_multiline(&list, shape, context)
} else {
// First we try formatting on one line.
rewrite_pairs_one_line(&list, shape, context)
.unknown_error()
.or_else(|_| rewrite_pairs_multiline(&list, shape, context))
}
})
}
// This may return a multi-line result since we allow the last expression to go
@ -65,7 +70,7 @@ fn rewrite_pairs_one_line<T: Rewrite>(
let base_shape = shape.block();
for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) {
if let Some(rewrite) = rewrite {
if let Ok(rewrite) = rewrite {
if !is_single_line(rewrite) || result.len() > shape.width {
return None;
}
@ -104,19 +109,20 @@ fn rewrite_pairs_multiline<T: Rewrite>(
list: &PairList<'_, '_, T>,
shape: Shape,
context: &RewriteContext<'_>,
) -> Option<String> {
) -> RewriteResult {
let rhs_offset = shape.rhs_overhead(context.config);
let nested_shape = (match context.config.indent_style() {
IndentStyle::Visual => shape.visual_indent(0),
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
})
.with_max_width(context.config)
.sub_width(rhs_offset)?;
.sub_width(rhs_offset)
.max_width_error(shape.width, list.span)?;
let indent_str = nested_shape.indent.to_string_with_newline(context.config);
let mut result = String::new();
result.push_str(list.list[0].1.as_ref()?);
result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?);
for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) {
// The following test checks if we should keep two subexprs on the same
@ -132,7 +138,7 @@ fn rewrite_pairs_multiline<T: Rewrite>(
if let Some(line_shape) =
shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result))
{
if let Some(rewrite) = e.rewrite(context, line_shape) {
if let Ok(rewrite) = e.rewrite_result(context, line_shape) {
result.push(' ');
result.push_str(s);
result.push(' ');
@ -155,9 +161,9 @@ fn rewrite_pairs_multiline<T: Rewrite>(
}
}
result.push_str(default_rw.as_ref()?);
result.push_str(default_rw.as_ref().map_err(|err| err.clone())?);
}
Some(result)
Ok(result)
}
// Rewrites a single pair.
@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair<LHS, RHS>(
context: &RewriteContext<'_>,
shape: Shape,
separator_place: SeparatorPlace,
) -> Option<String>
) -> RewriteResult
where
LHS: Rewrite,
RHS: Rewrite,
LHS: Rewrite + Spanned,
RHS: Rewrite + Spanned,
{
let tab_spaces = context.config.tab_spaces();
let lhs_overhead = match separator_place {
@ -183,15 +189,17 @@ where
..shape
};
let lhs_result = lhs
.rewrite(context, lhs_shape)
.rewrite_result(context, lhs_shape)
.map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?;
// Try to put both lhs and rhs on the same line.
let rhs_orig_result = shape
.offset_left(last_line_width(&lhs_result) + pp.infix.len())
.and_then(|s| s.sub_width(pp.suffix.len()))
.and_then(|rhs_shape| rhs.rewrite(context, rhs_shape));
if let Some(ref rhs_result) = rhs_orig_result {
.max_width_error(shape.width, rhs.span())
.and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape));
if let Ok(ref rhs_result) = rhs_orig_result {
// If the length of the lhs is equal to or shorter than the tab width or
// the rhs looks like block expression, we put the rhs on the same
// line with the lhs even if the rhs is multi-lined.
@ -207,7 +215,7 @@ where
+ first_line_width(rhs_result)
+ pp.suffix.len();
if one_line_width <= shape.width {
return Some(format!(
return Ok(format!(
"{}{}{}{}",
lhs_result, pp.infix, rhs_result, pp.suffix
));
@ -219,13 +227,15 @@ where
// Re-evaluate the rhs because we have more space now:
let mut rhs_shape = match context.config.indent_style() {
IndentStyle::Visual => shape
.sub_width(pp.suffix.len() + pp.prefix.len())?
.sub_width(pp.suffix.len() + pp.prefix.len())
.max_width_error(shape.width, rhs.span())?
.visual_indent(pp.prefix.len()),
IndentStyle::Block => {
// Try to calculate the initial constraint on the right hand side.
let rhs_overhead = shape.rhs_overhead(context.config);
Shape::indented(shape.indent.block_indent(context.config), context.config)
.sub_width(rhs_overhead)?
.sub_width(rhs_overhead)
.max_width_error(shape.width, rhs.span())?
}
};
let infix = match separator_place {
@ -233,15 +243,17 @@ where
SeparatorPlace::Front => pp.infix.trim_start(),
};
if separator_place == SeparatorPlace::Front {
rhs_shape = rhs_shape.offset_left(infix.len())?;
rhs_shape = rhs_shape
.offset_left(infix.len())
.max_width_error(rhs_shape.width, rhs.span())?;
}
let rhs_result = rhs.rewrite(context, rhs_shape)?;
let rhs_result = rhs.rewrite_result(context, rhs_shape)?;
let indent_str = rhs_shape.indent.to_string_with_newline(context.config);
let infix_with_sep = match separator_place {
SeparatorPlace::Back => format!("{infix}{indent_str}"),
SeparatorPlace::Front => format!("{indent_str}{infix}"),
};
Some(format!(
Ok(format!(
"{}{}{}{}",
lhs_result, infix_with_sep, rhs_result, pp.suffix
))
@ -255,8 +267,9 @@ trait FlattenPair: Rewrite + Sized {
}
struct PairList<'a, 'b, T: Rewrite> {
list: Vec<(&'b T, Option<String>)>,
list: Vec<(&'b T, RewriteResult)>,
separators: Vec<&'a str>,
span: Span,
}
fn is_ident(expr: &ast::Expr) -> bool {
@ -303,7 +316,7 @@ impl FlattenPair for ast::Expr {
let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
if is_first {
return node.rewrite(context, shape);
return node.rewrite_result(context, shape);
}
let nested_overhead = sep + 1;
let rhs_offset = shape.rhs_overhead(context.config);
@ -312,12 +325,17 @@ impl FlattenPair for ast::Expr {
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
})
.with_max_width(context.config)
.sub_width(rhs_offset)?;
.sub_width(rhs_offset)
.max_width_error(shape.width, node.span)?;
let default_shape = match context.config.binop_separator() {
SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?,
SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?,
SeparatorPlace::Back => nested_shape
.sub_width(nested_overhead)
.max_width_error(nested_shape.width, node.span)?,
SeparatorPlace::Front => nested_shape
.offset_left(nested_overhead)
.max_width_error(nested_shape.width, node.span)?,
};
node.rewrite(context, default_shape)
node.rewrite_result(context, default_shape)
};
// Turn a tree of binop expressions into a list using a depth-first,
@ -326,6 +344,7 @@ impl FlattenPair for ast::Expr {
let mut list = vec![];
let mut separators = vec![];
let mut node = self;
let span = self.span();
loop {
match node.kind {
ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => {
@ -352,7 +371,11 @@ impl FlattenPair for ast::Expr {
}
assert_eq!(list.len() - 1, separators.len());
Some(PairList { list, separators })
Some(PairList {
list,
separators,
span,
})
}
}

View file

@ -1,5 +1,5 @@
use rustc_ast::ast;
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
use crate::rewrite::RewriteContext;

View file

@ -1,4 +1,4 @@
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::panic::{AssertUnwindSafe, catch_unwind};
use rustc_ast::ast;
use rustc_ast::token::{Delimiter, TokenKind};

View file

@ -1,11 +1,11 @@
use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ast, ptr};
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_session::parse::ParseSess;
use rustc_span::symbol::{self, kw};
use rustc_span::Symbol;
use rustc_span::symbol::{self, kw};
use crate::macros::MacroArg;
use crate::rewrite::RewriteContext;

View file

@ -1,4 +1,4 @@
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::path::{Path, PathBuf};
use rustc_ast::token::TokenKind;
@ -6,11 +6,11 @@ use rustc_ast::{ast, attr, ptr};
use rustc_errors::Diag;
use rustc_parse::parser::Parser as RawParser;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_span::{sym, Span};
use rustc_span::{Span, sym};
use thin_vec::ThinVec;
use crate::parse::session::ParseSess;
use crate::Input;
use crate::parse::session::ParseSess;
pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;

View file

@ -2,13 +2,14 @@ use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter};
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
use rustc_errors::translation::Translate;
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
BytePos, Span,
source_map::{FilePathMapping, SourceMap},
symbol, BytePos, Span,
symbol,
};
use crate::config::file_lines::LineRange;

View file

@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd,
use rustc_ast::ptr;
use rustc_span::{BytePos, Span};
use crate::comment::{combine_strs_with_missing_comments, FindUncommented};
use crate::comment::{FindUncommented, combine_strs_with_missing_comments};
use crate::config::StyleEdition;
use crate::config::lists::*;
use crate::config::Version;
use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
use crate::lists::{
definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
struct_lit_tactic, write_list, ListFormatting, ListItem, Separator,
ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic,
struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
};
use crate::macros::{rewrite_macro, MacroPosition};
use crate::macros::{MacroPosition, rewrite_macro};
use crate::overflow;
use crate::pairs::{rewrite_pair, PairParts};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::pairs::{PairParts, rewrite_pair};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::types::{rewrite_path, PathContext};
use crate::types::{PathContext, rewrite_path};
use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident};
/// Returns `true` if the given pattern is "short".
@ -61,25 +61,36 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
}
}
struct RangeOperand<'a>(&'a Option<ptr::P<ast::Expr>>);
pub(crate) struct RangeOperand<'a> {
operand: &'a Option<ptr::P<ast::Expr>>,
pub(crate) span: Span,
}
impl<'a> Rewrite for RangeOperand<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
match &self.0 {
None => Some("".to_owned()),
Some(ref exp) => exp.rewrite(context, shape),
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match &self.operand {
None => Ok("".to_owned()),
Some(ref exp) => exp.rewrite_result(context, shape),
}
}
}
impl Rewrite for Pat {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self.kind {
PatKind::Or(ref pats) => {
let pat_strs = pats
.iter()
.map(|p| p.rewrite(context, shape))
.collect::<Option<Vec<_>>>()?;
.map(|p| p.rewrite_result(context, shape))
.collect::<Result<Vec<_>, RewriteError>>()?;
let use_mixed_layout = pats
.iter()
@ -115,14 +126,21 @@ impl Rewrite for Pat {
let sub_pat = match *sub_pat {
Some(ref p) => {
// 2 - `@ `.
let width = shape.width.checked_sub(
mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2,
)?;
let width = shape
.width
.checked_sub(
mut_prefix.len()
+ ref_kw.len()
+ mut_infix.len()
+ id_str.len()
+ 2,
)
.max_width_error(shape.width, p.span())?;
let lo = context.snippet_provider.span_after(self.span, "@");
combine_strs_with_missing_comments(
context,
"@",
&p.rewrite(context, Shape::legacy(width, shape.indent))?,
&p.rewrite_result(context, Shape::legacy(width, shape.indent))?,
mk_sp(lo, p.span.lo()),
shape,
true,
@ -207,19 +225,25 @@ impl Rewrite for Pat {
}
PatKind::Wild => {
if 1 <= shape.width {
Some("_".to_owned())
Ok("_".to_owned())
} else {
None
Err(RewriteError::ExceedsMaxWidth {
configured_width: 1,
span: self.span,
})
}
}
PatKind::Rest => {
if 1 <= shape.width {
Some("..".to_owned())
Ok("..".to_owned())
} else {
None
Err(RewriteError::ExceedsMaxWidth {
configured_width: 1,
span: self.span,
})
}
}
PatKind::Never => None,
PatKind::Never => Err(RewriteError::Unknown),
PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
let infix = match end_kind.node {
RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
@ -239,9 +263,17 @@ impl Rewrite for Pat {
} else {
infix.to_owned()
};
let lspan = self.span.with_hi(end_kind.span.lo());
let rspan = self.span.with_lo(end_kind.span.hi());
rewrite_pair(
&RangeOperand(lhs),
&RangeOperand(rhs),
&RangeOperand {
operand: lhs,
span: lspan,
},
&RangeOperand {
operand: rhs,
span: rspan,
},
PairParts::infix(&infix),
context,
shape,
@ -260,19 +292,21 @@ impl Rewrite for Pat {
let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?;
rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
}
PatKind::Lit(ref expr) => expr.rewrite(context, shape),
PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => {
PatKind::Lit(ref expr) => expr.rewrite_result(context, shape),
PatKind::Slice(ref slice_pat)
if context.config.style_edition() <= StyleEdition::Edition2021 =>
{
let rw: Vec<String> = slice_pat
.iter()
.map(|p| {
if let Some(rw) = p.rewrite(context, shape) {
if let Ok(rw) = p.rewrite_result(context, shape) {
rw
} else {
context.snippet(p.span).to_string()
}
})
.collect();
Some(format!("[{}]", rw.join(", ")))
Ok(format!("[{}]", rw.join(", ")))
}
PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets(
context,
@ -296,10 +330,16 @@ impl Rewrite for Pat {
rewrite_macro(mac, None, context, shape, MacroPosition::Pat)
}
PatKind::Paren(ref pat) => pat
.rewrite(context, shape.offset_left(1)?.sub_width(1)?)
.rewrite_result(
context,
shape
.offset_left(1)
.and_then(|s| s.sub_width(1))
.max_width_error(shape.width, self.span)?,
)
.map(|inner_pat| format!("({})", inner_pat)),
PatKind::Err(_) => None,
PatKind::Deref(_) => None,
PatKind::Err(_) => Err(RewriteError::Unknown),
PatKind::Deref(_) => Err(RewriteError::Unknown),
}
}
}
@ -312,20 +352,21 @@ fn rewrite_struct_pat(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
// 2 = ` {`
let path_shape = shape.sub_width(2)?;
let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
if fields.is_empty() && !ellipsis {
return Some(format!("{path_str} {{}}"));
return Ok(format!("{path_str} {{}}"));
}
let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
// 3 = ` { `, 2 = ` }`.
let (h_shape, v_shape) =
struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?;
struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)
.max_width_error(shape.width, span)?;
let items = itemize_list(
context.snippet_provider,
@ -340,7 +381,7 @@ fn rewrite_struct_pat(
}
},
|f| f.span.hi(),
|f| f.rewrite(context, v_shape),
|f| f.rewrite_result(context, v_shape),
context.snippet_provider.span_after(span, "{"),
span.hi(),
false,
@ -379,11 +420,15 @@ fn rewrite_struct_pat(
// ast::Pat doesn't have attrs so use &[]
let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
Some(format!("{path_str} {{{fields_str}}}"))
Ok(format!("{path_str} {{{fields_str}}}"))
}
impl Rewrite for PatField {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let hi_pos = if let Some(last) = self.attrs.last() {
last.span.hi()
} else {
@ -393,10 +438,10 @@ impl Rewrite for PatField {
let attrs_str = if self.attrs.is_empty() {
String::from("")
} else {
self.attrs.rewrite(context, shape)?
self.attrs.rewrite_result(context, shape)?
};
let pat_str = self.pat.rewrite(context, shape)?;
let pat_str = self.pat.rewrite_result(context, shape)?;
if self.is_shorthand {
combine_strs_with_missing_comments(
context,
@ -417,7 +462,7 @@ impl Rewrite for PatField {
"{}:\n{}{}",
id_str,
nested_shape.indent.to_string(context.config),
self.pat.rewrite(context, nested_shape)?
self.pat.rewrite_result(context, nested_shape)?
)
};
combine_strs_with_missing_comments(
@ -440,9 +485,13 @@ pub(crate) enum TuplePatField<'a> {
impl<'a> Rewrite for TuplePatField<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self {
TuplePatField::Pat(p) => p.rewrite(context, shape),
TuplePatField::Dotdot(_) => Some("..".to_string()),
TuplePatField::Pat(p) => p.rewrite_result(context, shape),
TuplePatField::Dotdot(_) => Ok("..".to_string()),
}
}
}
@ -492,9 +541,9 @@ fn rewrite_tuple_pat(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
if pats.is_empty() {
return Some(format!("{}()", path_str.unwrap_or_default()));
return Ok(format!("{}()", path_str.unwrap_or_default()));
}
let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect();
@ -548,7 +597,7 @@ fn count_wildcard_suffix_len(
",",
|item| item.span().lo(),
|item| item.span().hi(),
|item| item.rewrite(context, shape),
|item| item.rewrite_result(context, shape),
context.snippet_provider.span_after(span, "("),
span.hi() - BytePos(1),
false,
@ -558,7 +607,7 @@ fn count_wildcard_suffix_len(
for item in items
.iter()
.rev()
.take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_"))
.take_while(|i| matches!(i.item, Ok(ref internal_string) if internal_string == "_"))
{
suffix_len += 1;

View file

@ -9,13 +9,13 @@
use std::cmp::Ordering;
use rustc_ast::{ast, attr};
use rustc_span::{symbol::sym, Span};
use rustc_span::{Span, symbol::sym};
use crate::config::{Config, GroupImportsTactic};
use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree};
use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity};
use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
use crate::lists::{itemize_list, write_list, ListFormatting, ListItem};
use crate::rewrite::RewriteContext;
use crate::lists::{ListFormatting, ListItem, itemize_list, write_list};
use crate::rewrite::{RewriteContext, RewriteErrorExt};
use crate::shape::Shape;
use crate::source_map::LineRangeUtils;
use crate::spanned::Spanned;
@ -59,7 +59,7 @@ fn wrap_reorderable_items(
let fmt = ListFormatting::new(shape, context.config)
.separator("")
.align_comments(false);
write_list(list_items, &fmt)
write_list(list_items, &fmt).ok()
}
fn rewrite_reorderable_item(
@ -99,7 +99,7 @@ fn rewrite_reorderable_or_regroupable_items(
";",
|item| item.span().lo(),
|item| item.span().hi(),
|_item| Some("".to_owned()),
|_item| Ok("".to_owned()),
span.lo(),
span.hi(),
false,
@ -131,9 +131,16 @@ fn rewrite_reorderable_or_regroupable_items(
.map(|use_group| {
let item_vec: Vec<_> = use_group
.into_iter()
.map(|use_tree| ListItem {
item: use_tree.rewrite_top_level(context, nested_shape),
..use_tree.list_item.unwrap_or_else(ListItem::empty)
.map(|use_tree| {
let item = use_tree.rewrite_top_level(context, nested_shape);
if let Some(list_item) = use_tree.list_item {
ListItem {
item: item,
..list_item
}
} else {
ListItem::from_item(item)
}
})
.collect();
wrap_reorderable_items(context, &item_vec, nested_shape)
@ -151,7 +158,7 @@ fn rewrite_reorderable_or_regroupable_items(
";",
|item| item.span().lo(),
|item| item.span().hi(),
|item| rewrite_reorderable_item(context, item, shape),
|item| rewrite_reorderable_item(context, item, shape).unknown_error(),
span.lo(),
span.hi(),
false,

View file

@ -5,17 +5,23 @@ use std::rc::Rc;
use rustc_ast::ptr;
use rustc_span::Span;
use thiserror::Error;
use crate::FormatReport;
use crate::config::{Config, IndentStyle};
use crate::parse::session::ParseSess;
use crate::shape::Shape;
use crate::skip::SkipContext;
use crate::visitor::SnippetProvider;
use crate::FormatReport;
pub(crate) type RewriteResult = Result<String, RewriteError>;
pub(crate) trait Rewrite {
/// Rewrite self into shape.
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
self.rewrite(context, shape).unknown_error()
}
}
impl<T: Rewrite> Rewrite for ptr::P<T> {
@ -24,6 +30,66 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum MacroErrorKind {
ParseFailure,
ReplaceMacroVariable,
Unknown,
}
impl std::fmt::Display for MacroErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MacroErrorKind::ParseFailure => write!(f, "(parse failure)"),
MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"),
MacroErrorKind::Unknown => write!(f, ""),
}
}
}
#[derive(Clone, Error, Debug)]
pub(crate) enum RewriteError {
#[error("Formatting was skipped due to skip attribute or out of file range.")]
SkipFormatting,
#[error("It exceeds the required width of {configured_width} for the span: {span:?}")]
ExceedsMaxWidth { configured_width: usize, span: Span },
#[error("Failed to format given macro{} at: {span:?}", kind)]
MacroFailure { kind: MacroErrorKind, span: Span },
/// Format failure that does not fit to above categories.
#[error("An unknown error occurred during formatting.")]
Unknown,
}
/// Extension trait used to conveniently convert to RewriteError
pub(crate) trait RewriteErrorExt<T> {
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError>;
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError>;
fn unknown_error(self) -> Result<T, RewriteError>;
}
impl<T> RewriteErrorExt<T> for Option<T> {
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::ExceedsMaxWidth {
configured_width: width,
span: span,
})
}
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::MacroFailure {
kind: kind,
span: span,
})
}
fn unknown_error(self) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::Unknown)
}
}
#[derive(Clone)]
pub(crate) struct RewriteContext<'a> {
pub(crate) psess: &'a ParseSess,

View file

@ -282,7 +282,7 @@ where
#[cfg(test)]
mod test {
use super::DiffLine::*;
use super::{make_diff, Mismatch};
use super::{Mismatch, make_diff};
use super::{ModifiedChunk, ModifiedLines};
#[test]

368
src/sort.rs Normal file
View file

@ -0,0 +1,368 @@
use itertools::EitherOrBoth;
use itertools::Itertools;
/// Iterator which breaks an identifier into various [VersionChunk]s.
struct VersionChunkIter<'a> {
ident: &'a str,
start: usize,
}
impl<'a> VersionChunkIter<'a> {
pub(crate) fn new(ident: &'a str) -> Self {
Self { ident, start: 0 }
}
fn parse_numeric_chunk(
&mut self,
mut chars: std::str::CharIndices<'a>,
) -> Option<VersionChunk<'a>> {
let mut end = self.start;
let mut is_end_of_chunk = false;
while let Some((idx, c)) = chars.next() {
end = self.start + idx;
if c.is_ascii_digit() {
continue;
}
is_end_of_chunk = true;
break;
}
let source = if is_end_of_chunk {
let value = &self.ident[self.start..end];
self.start = end;
value
} else {
let value = &self.ident[self.start..];
self.start = self.ident.len();
value
};
let zeros = source.chars().take_while(|c| *c == '0').count();
let value = source.parse::<usize>().ok()?;
Some(VersionChunk::Number {
value,
zeros,
source,
})
}
fn parse_str_chunk(
&mut self,
mut chars: std::str::CharIndices<'a>,
) -> Option<VersionChunk<'a>> {
let mut end = self.start;
let mut is_end_of_chunk = false;
while let Some((idx, c)) = chars.next() {
end = self.start + idx;
if c == '_' {
is_end_of_chunk = true;
break;
}
if !c.is_numeric() {
continue;
}
is_end_of_chunk = true;
break;
}
let source = if is_end_of_chunk {
let value = &self.ident[self.start..end];
self.start = end;
value
} else {
let value = &self.ident[self.start..];
self.start = self.ident.len();
value
};
Some(VersionChunk::Str(source))
}
}
impl<'a> Iterator for VersionChunkIter<'a> {
type Item = VersionChunk<'a>;
fn next(&mut self) -> Option<Self::Item> {
let mut chars = self.ident[self.start..].char_indices();
let (_, next) = chars.next()?;
if next == '_' {
self.start = self.start + next.len_utf8();
return Some(VersionChunk::Underscore);
}
if next.is_ascii_digit() {
return self.parse_numeric_chunk(chars);
}
self.parse_str_chunk(chars)
}
}
/// Represents a chunk in the version-sort algorithm
#[derive(Debug, PartialEq, Eq)]
enum VersionChunk<'a> {
/// A single `_` in an identifier. Underscores are sorted before all other characters.
Underscore,
/// A &str chunk in the version sort.
Str(&'a str),
/// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
Number {
value: usize,
zeros: usize,
source: &'a str,
},
}
/// Determine which side of the version-sort comparison had more leading zeros.
#[derive(Debug, PartialEq, Eq)]
enum MoreLeadingZeros {
Left,
Right,
Equal,
}
/// Compare two identifiers based on the version sorting algorithm described in [the style guide]
///
/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting
pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering {
let iter_a = VersionChunkIter::new(a);
let iter_b = VersionChunkIter::new(b);
let mut more_leading_zeros = MoreLeadingZeros::Equal;
for either_or_both in iter_a.zip_longest(iter_b) {
match either_or_both {
EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
EitherOrBoth::Both(a, b) => match (a, b) {
(VersionChunk::Underscore, VersionChunk::Underscore) => {
continue;
}
(VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
(_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
(VersionChunk::Str(ca), VersionChunk::Str(cb))
| (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
| (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
match ca.cmp(&cb) {
std::cmp::Ordering::Equal => {
continue;
}
order @ _ => return order,
}
}
(
VersionChunk::Number {
value: va,
zeros: lza,
..
},
VersionChunk::Number {
value: vb,
zeros: lzb,
..
},
) => match va.cmp(&vb) {
std::cmp::Ordering::Equal => {
if lza == lzb {
continue;
}
if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
more_leading_zeros = MoreLeadingZeros::Left;
} else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
more_leading_zeros = MoreLeadingZeros::Right;
}
continue;
}
order @ _ => return order,
},
},
}
}
match more_leading_zeros {
MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
MoreLeadingZeros::Left => std::cmp::Ordering::Less,
MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_chunks() {
let mut iter = VersionChunkIter::new("x86_128");
assert_eq!(iter.next(), Some(VersionChunk::Str("x")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 86,
zeros: 0,
source: "86"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 128,
zeros: 0,
source: "128"
})
);
assert_eq!(iter.next(), None);
let mut iter = VersionChunkIter::new("w005s09t");
assert_eq!(iter.next(), Some(VersionChunk::Str("w")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 5,
zeros: 2,
source: "005"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("s")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 9,
zeros: 1,
source: "09"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("t")));
assert_eq!(iter.next(), None);
let mut iter = VersionChunkIter::new("ZY_WX");
assert_eq!(iter.next(), Some(VersionChunk::Str("ZY")));
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(iter.next(), Some(VersionChunk::Str("WX")));
let mut iter = VersionChunkIter::new("_v1");
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 1,
zeros: 0,
source: "1"
})
);
let mut iter = VersionChunkIter::new("_1v");
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 1,
zeros: 0,
source: "1"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
let mut iter = VersionChunkIter::new("v009");
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 9,
zeros: 2,
source: "009"
})
);
}
#[test]
fn test_version_sort() {
let mut input = vec!["", "b", "a"];
let expected = vec!["", "a", "b"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["x7x", "xxx"];
let expected = vec!["x7x", "xxx"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["applesauce", "apple"];
let expected = vec!["apple", "applesauce"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["aaaaa", "aaa_a"];
let expected = vec!["aaa_a", "aaaaa"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"];
let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["1_000_000", "1_010_001"];
let expected = vec!["1_000_000", "1_010_001"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec![
"5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050",
"50_500",
];
let expected = vec![
"5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500",
"500",
];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"];
let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["__", "_"];
let expected = vec!["_", "__"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["foo_", "foo"];
let expected = vec!["foo", "foo_"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec![
"x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01",
"v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW",
"ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32",
"ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128",
"u256",
];
let expected = vec![
"_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1",
"abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz",
"v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9",
"v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128",
"x87", "zyxw",
];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected)
}
}

View file

@ -2,10 +2,10 @@ use std::fs;
use std::io::{self, Write};
use std::path::Path;
use crate::NewlineStyle;
use crate::config::FileName;
use crate::emitter::{self, Emitter};
use crate::parse::session::ParseSess;
use crate::NewlineStyle;
#[cfg(test)]
use crate::config::Config;

View file

@ -1,9 +1,10 @@
use std::cmp::max;
use rustc_ast::{ast, ptr};
use rustc_span::{source_map, Span};
use rustc_span::{Span, source_map};
use crate::macros::MacroArg;
use crate::patterns::RangeOperand;
use crate::utils::{mk_sp, outer_attributes};
/// Spanned returns a span including attributes, if available.
@ -212,3 +213,9 @@ impl Spanned for ast::PreciseCapturingArg {
}
}
}
impl<'a> Spanned for RangeOperand<'a> {
fn span(&self) -> Span {
self.span
}
}

View file

@ -2,9 +2,9 @@ use rustc_ast::ast;
use rustc_span::Span;
use crate::comment::recover_comment_removed;
use crate::config::Version;
use crate::expr::{format_expr, is_simple_block, ExprType};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::config::StyleEdition;
use crate::expr::{ExprType, format_expr, is_simple_block};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::LineRangeUtils;
use crate::spanned::Spanned;
@ -90,11 +90,20 @@ impl<'a> Stmt<'a> {
impl<'a> Rewrite for Stmt<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let expr_type = if context.config.version() == Version::Two && self.is_last_expr() {
ExprType::SubExpression
} else {
ExprType::Statement
};
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(
&self,
context: &RewriteContext<'_>,
shape: Shape,
) -> crate::rewrite::RewriteResult {
let expr_type =
if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() {
ExprType::SubExpression
} else {
ExprType::Statement
};
format_stmt(
context,
shape,
@ -111,11 +120,11 @@ fn format_stmt(
stmt: &ast::Stmt,
expr_type: ExprType,
is_last_expr: bool,
) -> Option<String> {
skip_out_of_file_lines_range!(context, stmt.span());
) -> RewriteResult {
skip_out_of_file_lines_range_err!(context, stmt.span());
let result = match stmt.kind {
ast::StmtKind::Let(ref local) => local.rewrite(context, shape),
ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape),
ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
";"
@ -123,10 +132,14 @@ fn format_stmt(
""
};
let shape = shape.sub_width(suffix.len())?;
let shape = shape
.sub_width(suffix.len())
.max_width_error(shape.width, ex.span())?;
format_expr(ex, expr_type, context, shape).map(|s| s + suffix)
}
ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None,
ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => {
Err(RewriteError::Unknown)
}
};
result.and_then(|res| recover_comment_removed(res, stmt.span(), context))
result.map(|res| recover_comment_removed(res, stmt.span(), context))
}

View file

@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize {
#[cfg(test)]
mod test {
use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat};
use super::{SnippetState, StringFormat, break_string, detect_url, rewrite_string};
use crate::config::Config;
use crate::shape::{Indent, Shape};
use unicode_segmentation::UnicodeSegmentation;

View file

@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write};
use std::iter::Enumerate;
use std::path::{Path, PathBuf};
use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE};
use super::{DIFF_CONTEXT_SIZE, print_mismatches, write_message};
use crate::config::{Config, EmitMode, Verbosity};
use crate::rustfmt_diff::{make_diff, Mismatch};
use crate::rustfmt_diff::{Mismatch, make_diff};
use crate::{Input, Session};
const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";

View file

@ -6,14 +6,17 @@ use std::iter::Peekable;
use std::mem;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str::Chars;
use std::str::{Chars, FromStr};
use std::thread;
use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle};
use crate::formatting::{ReportedErrors, SourceFile};
use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter};
use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff};
use crate::source_file;
use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session};
use crate::{
Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, StyleEdition, Version,
is_nightly_channel,
};
use rustfmt_config_proc_macro::nightly_only_test;
use tracing::{debug, warn};
@ -711,13 +714,24 @@ fn print_mismatches<T: Fn(u32) -> String>(
fn read_config(filename: &Path) -> Config {
let sig_comments = read_significant_comments(filename);
let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
// Look for a config file. If there is a 'config' property in the significant comments, use
// that. Otherwise, if there are no significant comments at all, look for a config file with
// the same name as the test file.
let mut config = if !sig_comments.is_empty() {
get_config(sig_comments.get("config").map(Path::new))
get_config(
sig_comments.get("config").map(Path::new),
edition,
style_edition,
version,
)
} else {
get_config(filename.with_extension("toml").file_name().map(Path::new))
get_config(
filename.with_extension("toml").file_name().map(Path::new),
edition,
style_edition,
version,
)
};
for (key, val) in &sig_comments {
@ -748,13 +762,31 @@ enum IdempotentCheckError {
Parse,
}
fn get_editions_from_comments(
comments: &HashMap<String, String>,
) -> (Option<Edition>, Option<StyleEdition>, Option<Version>) {
(
comments
.get("edition")
.map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))),
comments.get("style_edition").map(|se| {
StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se))
}),
comments
.get("version")
.map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))),
)
}
fn idempotent_check(
filename: &PathBuf,
opt_config: &Option<PathBuf>,
) -> Result<FormatReport, IdempotentCheckError> {
let sig_comments = read_significant_comments(filename);
let config = if let Some(ref config_file_path) = opt_config {
Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found")
let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
Config::from_toml_path(config_file_path, edition, style_edition, version)
.expect("`rustfmt.toml` not found")
} else {
read_config(filename)
};
@ -778,14 +810,19 @@ fn idempotent_check(
// Reads test config file using the supplied (optional) file name. If there's no file name or the
// file doesn't exist, just return the default config. Otherwise, the file must be read
// successfully.
fn get_config(config_file: Option<&Path>) -> Config {
fn get_config(
config_file: Option<&Path>,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Config {
let config_file_name = match config_file {
None => return Default::default(),
None => return Config::default_for_possible_style_edition(style_edition, edition, version),
Some(file_name) => {
let mut full_path = PathBuf::from("tests/config/");
full_path.push(file_name);
if !full_path.exists() {
return Default::default();
return Config::default_for_possible_style_edition(style_edition, edition, version);
};
full_path
}
@ -797,7 +834,14 @@ fn get_config(config_file: Option<&Path>) -> Config {
.read_to_string(&mut def_config)
.expect("Couldn't read config");
Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML")
Config::from_toml_for_style_edition(
&def_config,
Path::new("tests/config/"),
edition,
style_edition,
version,
)
.expect("invalid TOML")
}
// Reads significant comments of the form: `// rustfmt-key: value` into a hash map.

View file

@ -2,23 +2,23 @@ use std::ops::Deref;
use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
use rustc_ast::ptr;
use rustc_span::{symbol::kw, BytePos, Pos, Span};
use rustc_span::{BytePos, Pos, Span, symbol::kw};
use tracing::debug;
use crate::comment::{combine_strs_with_missing_comments, contains_comment};
use crate::config::lists::*;
use crate::config::{IndentStyle, TypeDensity, Version};
use crate::config::{IndentStyle, StyleEdition, TypeDensity};
use crate::expr::{
format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType,
RhsAssignKind,
ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple,
rewrite_unary_prefix,
};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
};
use crate::macros::{rewrite_macro, MacroPosition};
use crate::macros::{MacroPosition, rewrite_macro};
use crate::overflow;
use crate::pairs::{rewrite_pair, PairParts};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::pairs::{PairParts, rewrite_pair};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
@ -41,7 +41,7 @@ pub(crate) fn rewrite_path(
qself: &Option<ptr::P<ast::QSelf>>,
path: &ast::Path,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let skip_count = qself.as_ref().map_or(0, |x| x.position);
// 32 covers almost all path lengths measured when compiling core, and there isn't a big
@ -57,7 +57,7 @@ pub(crate) fn rewrite_path(
if let Some(qself) = qself {
result.push('<');
let fmt_ty = qself.ty.rewrite(context, shape)?;
let fmt_ty = qself.ty.rewrite_result(context, shape)?;
result.push_str(&fmt_ty);
if skip_count > 0 {
@ -67,7 +67,7 @@ pub(crate) fn rewrite_path(
}
// 3 = ">::".len()
let shape = shape.sub_width(3)?;
let shape = shape.sub_width(3).max_width_error(shape.width, path.span)?;
result = rewrite_path_segments(
PathContext::Type,
@ -103,7 +103,7 @@ fn rewrite_path_segments<'a, I>(
span_hi: BytePos,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String>
) -> RewriteResult
where
I: Iterator<Item = &'a ast::PathSegment>,
{
@ -122,7 +122,9 @@ where
}
let extra_offset = extra_offset(&buffer, shape);
let new_shape = shape.shrink_left(extra_offset)?;
let new_shape = shape
.shrink_left(extra_offset)
.max_width_error(shape.width, mk_sp(span_lo, span_hi))?;
let segment_string = rewrite_segment(
path_context,
segment,
@ -135,7 +137,7 @@ where
buffer.push_str(&segment_string);
}
Some(buffer)
Ok(buffer)
}
#[derive(Debug)]
@ -169,19 +171,27 @@ impl<'a> Spanned for SegmentParam<'a> {
impl<'a> Rewrite for SegmentParam<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self {
SegmentParam::Const(const_) => const_.rewrite(context, shape),
SegmentParam::LifeTime(lt) => lt.rewrite(context, shape),
SegmentParam::Type(ty) => ty.rewrite(context, shape),
SegmentParam::Binding(atc) => atc.rewrite(context, shape),
SegmentParam::Const(const_) => const_.rewrite_result(context, shape),
SegmentParam::LifeTime(lt) => lt.rewrite_result(context, shape),
SegmentParam::Type(ty) => ty.rewrite_result(context, shape),
SegmentParam::Binding(atc) => atc.rewrite_result(context, shape),
}
}
}
impl Rewrite for ast::PreciseCapturingArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self {
ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite(context, shape),
ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite_result(context, shape),
ast::PreciseCapturingArg::Arg(p, _) => {
rewrite_path(context, PathContext::Type, &None, p, shape)
}
@ -191,13 +201,20 @@ impl Rewrite for ast::PreciseCapturingArg {
impl Rewrite for ast::AssocItemConstraint {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
use ast::AssocItemConstraintKind::{Bound, Equality};
let mut result = String::with_capacity(128);
result.push_str(rewrite_ident(context, self.ident));
if let Some(ref gen_args) = self.gen_args {
let budget = shape.width.checked_sub(result.len())?;
let budget = shape
.width
.checked_sub(result.len())
.max_width_error(shape.width, self.span)?;
let shape = Shape::legacy(budget, shape.indent + result.len());
let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?;
result.push_str(&gen_str);
@ -210,23 +227,30 @@ impl Rewrite for ast::AssocItemConstraint {
};
result.push_str(infix);
let budget = shape.width.checked_sub(result.len())?;
let budget = shape
.width
.checked_sub(result.len())
.max_width_error(shape.width, self.span)?;
let shape = Shape::legacy(budget, shape.indent + result.len());
let rewrite = self.kind.rewrite(context, shape)?;
let rewrite = self.kind.rewrite_result(context, shape)?;
result.push_str(&rewrite);
Some(result)
Ok(result)
}
}
impl Rewrite for ast::AssocItemConstraintKind {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self {
ast::AssocItemConstraintKind::Equality { term } => match term {
Term::Ty(ty) => ty.rewrite(context, shape),
Term::Const(c) => c.rewrite(context, shape),
Term::Ty(ty) => ty.rewrite_result(context, shape),
Term::Const(c) => c.rewrite_result(context, shape),
},
ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite(context, shape),
ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite_result(context, shape),
}
}
}
@ -248,16 +272,17 @@ fn rewrite_segment(
span_hi: BytePos,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
let mut result = String::with_capacity(128);
result.push_str(rewrite_ident(context, segment.ident));
let ident_len = result.len();
let shape = if context.use_block_indent() {
shape.offset_left(ident_len)?
shape.offset_left(ident_len)
} else {
shape.shrink_left(ident_len)?
};
shape.shrink_left(ident_len)
}
.max_width_error(shape.width, mk_sp(*span_lo, span_hi))?;
if let Some(ref args) = segment.args {
let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?;
@ -288,7 +313,7 @@ fn rewrite_segment(
result.push_str(&generics_str)
}
Some(result)
Ok(result)
}
fn format_function_type<'a, I>(
@ -298,7 +323,7 @@ fn format_function_type<'a, I>(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String>
) -> RewriteResult
where
I: ExactSizeIterator,
<I as Iterator>::Item: Deref,
@ -308,12 +333,12 @@ where
let ty_shape = match context.config.indent_style() {
// 4 = " -> "
IndentStyle::Block => shape.offset_left(4)?,
IndentStyle::Visual => shape.block_left(4)?,
IndentStyle::Block => shape.offset_left(4).max_width_error(shape.width, span)?,
IndentStyle::Visual => shape.block_left(4).max_width_error(shape.width, span)?,
};
let output = match *output {
FnRetTy::Ty(ref ty) => {
let type_str = ty.rewrite(context, ty_shape)?;
let type_str = ty.rewrite_result(context, ty_shape)?;
format!(" -> {type_str}")
}
FnRetTy::Default(..) => String::new(),
@ -326,7 +351,10 @@ where
)
} else {
// 2 for ()
let budget = shape.width.checked_sub(2)?;
let budget = shape
.width
.checked_sub(2)
.max_width_error(shape.width, span)?;
// 1 for (
let offset = shape.indent + 1;
Shape::legacy(budget, offset)
@ -339,7 +367,8 @@ where
let list_hi = context.snippet_provider.span_before(span, ")");
let comment = context
.snippet_provider
.span_to_snippet(mk_sp(list_lo, list_hi))?
.span_to_snippet(mk_sp(list_lo, list_hi))
.unknown_error()?
.trim();
let comment = if comment.starts_with("//") {
format!(
@ -360,7 +389,7 @@ where
",",
|arg| arg.span().lo(),
|arg| arg.span().hi(),
|arg| arg.rewrite(context, list_shape),
|arg| arg.rewrite_result(context, list_shape),
list_lo,
span.hi(),
false,
@ -396,9 +425,9 @@ where
)
};
if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width {
Some(format!("{args}{output}"))
Ok(format!("{args}{output}"))
} else {
Some(format!(
Ok(format!(
"{}\n{}{}",
args,
list_shape.indent.to_string(context.config),
@ -429,6 +458,10 @@ fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveL
impl Rewrite for ast::WherePredicate {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
// FIXME: dead spans?
let result = match *self {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
@ -437,7 +470,7 @@ impl Rewrite for ast::WherePredicate {
ref bounds,
..
}) => {
let type_str = bounded_ty.rewrite(context, shape)?;
let type_str = bounded_ty.rewrite_result(context, shape)?;
let colon = type_bound_colon(context).trim_end();
let lhs = if let Some(binder_str) =
rewrite_bound_params(context, shape, bound_generic_params)
@ -452,28 +485,34 @@ impl Rewrite for ast::WherePredicate {
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
ref lifetime,
ref bounds,
..
}) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?,
span,
}) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?,
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
ref lhs_ty,
ref rhs_ty,
..
}) => {
let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?;
let lhs_ty_str = lhs_ty
.rewrite_result(context, shape)
.map(|lhs| lhs + " =")?;
rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)?
}
};
Some(result)
Ok(result)
}
}
impl Rewrite for ast::GenericArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self {
ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape),
ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape),
ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape),
ast::GenericArg::Lifetime(ref lt) => lt.rewrite_result(context, shape),
ast::GenericArg::Type(ref ty) => ty.rewrite_result(context, shape),
ast::GenericArg::Const(ref const_) => const_.rewrite_result(context, shape),
}
}
}
@ -483,11 +522,11 @@ fn rewrite_generic_args(
context: &RewriteContext<'_>,
shape: Shape,
span: Span,
) -> Option<String> {
) -> RewriteResult {
match gen_args {
ast::GenericArgs::AngleBracketed(ref data) => {
if data.args.is_empty() {
Some("".to_owned())
Ok("".to_owned())
} else {
let args = data
.args
@ -513,47 +552,63 @@ fn rewrite_generic_args(
context,
shape,
),
ast::GenericArgs::ParenthesizedElided(..) => Some("(..)".to_owned()),
ast::GenericArgs::ParenthesizedElided(..) => Ok("(..)".to_owned()),
}
}
fn rewrite_bounded_lifetime(
lt: &ast::Lifetime,
bounds: &[ast::GenericBound],
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
let result = lt.rewrite(context, shape)?;
) -> RewriteResult {
let result = lt.rewrite_result(context, shape)?;
if bounds.is_empty() {
Some(result)
Ok(result)
} else {
let colon = type_bound_colon(context);
let overhead = last_line_width(&result) + colon.len();
let shape = shape
.sub_width(overhead)
.max_width_error(shape.width, span)?;
let result = format!(
"{}{}{}",
result,
colon,
join_bounds(context, shape.sub_width(overhead)?, bounds, true)?
join_bounds(context, shape, bounds, true)?
);
Some(result)
Ok(result)
}
}
impl Rewrite for ast::AnonConst {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
format_expr(&self.value, ExprType::SubExpression, context, shape)
}
}
impl Rewrite for ast::Lifetime {
fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> {
Some(context.snippet(self.ident.span).to_owned())
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, _: Shape) -> RewriteResult {
Ok(context.snippet(self.ident.span).to_owned())
}
}
impl Rewrite for ast::GenericBound {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self {
ast::GenericBound::Trait(
ref poly_trait_ref,
@ -574,24 +629,30 @@ impl Rewrite for ast::GenericBound {
asyncness.push(' ');
}
let polarity = polarity.as_str();
let shape = shape.offset_left(constness.len() + polarity.len())?;
let shape = shape
.offset_left(constness.len() + polarity.len())
.max_width_error(shape.width, self.span())?;
poly_trait_ref
.rewrite(context, shape)
.rewrite_result(context, shape)
.map(|s| format!("{constness}{asyncness}{polarity}{s}"))
.map(|s| if has_paren { format!("({})", s) } else { s })
}
ast::GenericBound::Use(ref args, span) => {
overflow::rewrite_with_angle_brackets(context, "use", args.iter(), shape, span)
}
ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape),
ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite_result(context, shape),
}
}
}
impl Rewrite for ast::GenericBounds {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if self.is_empty() {
return Some(String::new());
return Ok(String::new());
}
join_bounds(context, shape, self, true)
@ -600,8 +661,15 @@ impl Rewrite for ast::GenericBounds {
impl Rewrite for ast::GenericParam {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
// FIXME: If there are more than one attributes, this will force multiline.
let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new());
let mut result = self
.attrs
.rewrite_result(context, shape)
.unwrap_or(String::new());
let has_attrs = !result.is_empty();
let mut param = String::with_capacity(128);
@ -615,15 +683,19 @@ impl Rewrite for ast::GenericParam {
param.push_str("const ");
param.push_str(rewrite_ident(context, self.ident));
param.push_str(": ");
param.push_str(&ty.rewrite(context, shape)?);
param.push_str(&ty.rewrite_result(context, shape)?);
if let Some(default) = default {
let eq_str = match context.config.type_punctuation_density() {
TypeDensity::Compressed => "=",
TypeDensity::Wide => " = ",
};
param.push_str(eq_str);
let budget = shape.width.checked_sub(param.len())?;
let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?;
let budget = shape
.width
.checked_sub(param.len())
.max_width_error(shape.width, self.span())?;
let rewrite =
default.rewrite_result(context, Shape::legacy(budget, shape.indent))?;
param.push_str(&rewrite);
}
kw_span.lo()
@ -634,7 +706,7 @@ impl Rewrite for ast::GenericParam {
if !self.bounds.is_empty() {
param.push_str(type_bound_colon(context));
param.push_str(&self.bounds.rewrite(context, shape)?)
param.push_str(&self.bounds.rewrite_result(context, shape)?)
}
if let ast::GenericParamKind::Type {
default: Some(ref def),
@ -645,9 +717,12 @@ impl Rewrite for ast::GenericParam {
TypeDensity::Wide => " = ",
};
param.push_str(eq_str);
let budget = shape.width.checked_sub(param.len())?;
let budget = shape
.width
.checked_sub(param.len())
.max_width_error(shape.width, self.span())?;
let rewrite =
def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?;
def.rewrite_result(context, Shape::legacy(budget, shape.indent + param.len()))?;
param.push_str(&rewrite);
}
@ -673,44 +748,67 @@ impl Rewrite for ast::GenericParam {
result.push_str(&param);
}
Some(result)
Ok(result)
}
}
impl Rewrite for ast::PolyTraitRef {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params)
{
// 6 is "for<> ".len()
let extra_offset = lifetime_str.len() + 6;
let path_str = self
.trait_ref
.rewrite(context, shape.offset_left(extra_offset)?)?;
let shape = shape
.offset_left(extra_offset)
.max_width_error(shape.width, self.span)?;
let path_str = self.trait_ref.rewrite_result(context, shape)?;
Some(format!("for<{lifetime_str}> {path_str}"))
Ok(format!("for<{lifetime_str}> {path_str}"))
} else {
self.trait_ref.rewrite(context, shape)
self.trait_ref.rewrite_result(context, shape)
}
}
}
impl Rewrite for ast::TraitRef {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
rewrite_path(context, PathContext::Type, &None, &self.path, shape)
}
}
impl Rewrite for ast::Ty {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self.kind {
ast::TyKind::TraitObject(ref bounds, tobj_syntax) => {
// we have to consider 'dyn' keyword is used or not!!!
let (shape, prefix) = match tobj_syntax {
ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "),
ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "),
ast::TraitObjectSyntax::Dyn => {
let shape = shape
.offset_left(4)
.max_width_error(shape.width, self.span())?;
(shape, "dyn ")
}
ast::TraitObjectSyntax::DynStar => {
let shape = shape
.offset_left(5)
.max_width_error(shape.width, self.span())?;
(shape, "dyn* ")
}
ast::TraitObjectSyntax::None => (shape, ""),
};
let mut res = bounds.rewrite(context, shape)?;
let mut res = bounds.rewrite_result(context, shape)?;
// We may have falsely removed a trailing `+` inside macro call.
if context.inside_macro()
&& bounds.len() == 1
@ -719,7 +817,7 @@ impl Rewrite for ast::Ty {
{
res.push('+');
}
Some(format!("{prefix}{res}"))
Ok(format!("{prefix}{res}"))
}
ast::TyKind::Ptr(ref mt) => {
let prefix = match mt.mutbl {
@ -738,8 +836,11 @@ impl Rewrite for ast::Ty {
let mut cmnt_lo = ref_hi;
if let Some(ref lifetime) = *lifetime {
let lt_budget = shape.width.checked_sub(2 + mut_len)?;
let lt_str = lifetime.rewrite(
let lt_budget = shape
.width
.checked_sub(2 + mut_len)
.max_width_error(shape.width, self.span())?;
let lt_str = lifetime.rewrite_result(
context,
Shape::legacy(lt_budget, shape.indent + 2 + mut_len),
)?;
@ -783,39 +884,46 @@ impl Rewrite for ast::Ty {
result = combine_strs_with_missing_comments(
context,
result.trim_end(),
&mt.ty.rewrite(context, shape)?,
&mt.ty.rewrite_result(context, shape)?,
before_ty_span,
shape,
true,
)?;
} else {
let used_width = last_line_width(&result);
let budget = shape.width.checked_sub(used_width)?;
let ty_str = mt
.ty
.rewrite(context, Shape::legacy(budget, shape.indent + used_width))?;
let budget = shape
.width
.checked_sub(used_width)
.max_width_error(shape.width, self.span())?;
let ty_str = mt.ty.rewrite_result(
context,
Shape::legacy(budget, shape.indent + used_width),
)?;
result.push_str(&ty_str);
}
Some(result)
Ok(result)
}
// FIXME: we drop any comments here, even though it's a silly place to put
// comments.
ast::TyKind::Paren(ref ty) => {
if context.config.version() == Version::One
if context.config.style_edition() <= StyleEdition::Edition2021
|| context.config.indent_style() == IndentStyle::Visual
{
let budget = shape.width.checked_sub(2)?;
let budget = shape
.width
.checked_sub(2)
.max_width_error(shape.width, self.span())?;
return ty
.rewrite(context, Shape::legacy(budget, shape.indent + 1))
.rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
.map(|ty_str| format!("({})", ty_str));
}
// 2 = ()
if let Some(sh) = shape.sub_width(2) {
if let Some(ref s) = ty.rewrite(context, sh) {
if let Ok(ref s) = ty.rewrite_result(context, sh) {
if !s.contains('\n') {
return Some(format!("({s})"));
return Ok(format!("({s})"));
}
}
}
@ -824,8 +932,8 @@ impl Rewrite for ast::Ty {
let shape = shape
.block_indent(context.config.tab_spaces())
.with_max_width(context.config);
let rw = ty.rewrite(context, shape)?;
Some(format!(
let rw = ty.rewrite_result(context, shape)?;
Ok(format!(
"({}{}{})",
shape.to_string_with_newline(context.config),
rw,
@ -833,15 +941,18 @@ impl Rewrite for ast::Ty {
))
}
ast::TyKind::Slice(ref ty) => {
let budget = shape.width.checked_sub(4)?;
ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
let budget = shape
.width
.checked_sub(4)
.max_width_error(shape.width, self.span())?;
ty.rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
.map(|ty_str| format!("[{}]", ty_str))
}
ast::TyKind::Tup(ref items) => {
rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
}
ast::TyKind::AnonStruct(..) => Some(context.snippet(self.span).to_owned()),
ast::TyKind::AnonUnion(..) => Some(context.snippet(self.span).to_owned()),
ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::Path(ref q_self, ref path) => {
rewrite_path(context, PathContext::Type, q_self, path, shape)
}
@ -855,24 +966,27 @@ impl Rewrite for ast::Ty {
),
ast::TyKind::Infer => {
if shape.width >= 1 {
Some("_".to_owned())
Ok("_".to_owned())
} else {
None
Err(RewriteError::ExceedsMaxWidth {
configured_width: shape.width,
span: self.span(),
})
}
}
ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
ast::TyKind::Never => Some(String::from("!")),
ast::TyKind::Never => Ok(String::from("!")),
ast::TyKind::MacCall(ref mac) => {
rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
}
ast::TyKind::ImplicitSelf => Some(String::from("")),
ast::TyKind::ImplicitSelf => Ok(String::from("")),
ast::TyKind::ImplTrait(_, ref it) => {
// Empty trait is not a parser error.
if it.is_empty() {
return Some("impl".to_owned());
return Ok("impl".to_owned());
}
let rw = if context.config.version() == Version::One {
it.rewrite(context, shape)
let rw = if context.config.style_edition() <= StyleEdition::Edition2021 {
it.rewrite_result(context, shape)
} else {
join_bounds(context, shape, it, false)
};
@ -881,8 +995,8 @@ impl Rewrite for ast::Ty {
format!("impl{}{}", space, it_str)
})
}
ast::TyKind::CVarArgs => Some("...".to_owned()),
ast::TyKind::Dummy | ast::TyKind::Err(_) => Some(context.snippet(self.span).to_owned()),
ast::TyKind::CVarArgs => Ok("...".to_owned()),
ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::Typeof(ref anon_const) => rewrite_call(
context,
"typeof",
@ -891,9 +1005,9 @@ impl Rewrite for ast::Ty {
shape,
),
ast::TyKind::Pat(ref ty, ref pat) => {
let ty = ty.rewrite(context, shape)?;
let pat = pat.rewrite(context, shape)?;
Some(format!("{ty} is {pat}"))
let ty = ty.rewrite_result(context, shape)?;
let pat = pat.rewrite_result(context, shape)?;
Ok(format!("{ty} is {pat}"))
}
}
}
@ -904,7 +1018,7 @@ fn rewrite_bare_fn(
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
) -> RewriteResult {
debug!("rewrite_bare_fn {:#?}", shape);
let mut result = String::with_capacity(128);
@ -928,9 +1042,14 @@ fn rewrite_bare_fn(
result.push_str("fn");
let func_ty_shape = if context.use_block_indent() {
shape.offset_left(result.len())?
shape
.offset_left(result.len())
.max_width_error(shape.width, span)?
} else {
shape.visual_indent(result.len()).sub_width(result.len())?
shape
.visual_indent(result.len())
.sub_width(result.len())
.max_width_error(shape.width, span)?
};
let rewrite = format_function_type(
@ -944,7 +1063,7 @@ fn rewrite_bare_fn(
result.push_str(&rewrite);
Some(result)
Ok(result)
}
fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool {
@ -968,7 +1087,7 @@ fn join_bounds(
shape: Shape,
items: &[ast::GenericBound],
need_indent: bool,
) -> Option<String> {
) -> RewriteResult {
join_bounds_inner(context, shape, items, need_indent, false)
}
@ -978,7 +1097,7 @@ fn join_bounds_inner(
items: &[ast::GenericBound],
need_indent: bool,
force_newline: bool,
) -> Option<String> {
) -> RewriteResult {
debug_assert!(!items.is_empty());
let generic_bounds_in_order = is_generic_bounds_in_order(items);
@ -1073,10 +1192,10 @@ fn join_bounds_inner(
};
let (extendable, trailing_str) = if i == 0 {
let bound_str = item.rewrite(context, shape)?;
let bound_str = item.rewrite_result(context, shape)?;
(is_bound_extendable(&bound_str, item), bound_str)
} else {
let bound_str = &item.rewrite(context, shape)?;
let bound_str = &item.rewrite_result(context, shape)?;
match leading_span {
Some(ls) if has_leading_comment => (
is_bound_extendable(bound_str, item),
@ -1100,7 +1219,7 @@ fn join_bounds_inner(
true,
)
.map(|v| (v, trailing_span, extendable)),
_ => Some((strs + &trailing_str, trailing_span, extendable)),
_ => Ok((strs + &trailing_str, trailing_span, extendable)),
}
},
)?;
@ -1110,22 +1229,22 @@ fn join_bounds_inner(
// 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 => {
let retry_with_force_newline = match context.config.style_edition() {
style_edition @ _ if style_edition <= StyleEdition::Edition2021 => {
!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 force_newline => false,
_ if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
_ if items.len() > 1 => true,
_ => 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)
Ok(result.0)
}
}

View file

@ -6,11 +6,11 @@ use rustc_ast::ast::{
};
use rustc_ast::ptr;
use rustc_ast_pretty::pprust;
use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext};
use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol};
use unicode_width::UnicodeWidthStr;
use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses};
use crate::config::{Config, Version};
use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code};
use crate::config::{Config, StyleEdition};
use crate::rewrite::RewriteContext;
use crate::shape::{Indent, Shape};
@ -367,10 +367,10 @@ macro_rules! out_of_file_lines_range {
};
}
macro_rules! skip_out_of_file_lines_range {
macro_rules! skip_out_of_file_lines_range_err {
($self:ident, $span:expr) => {
if out_of_file_lines_range!($self, $span) {
return None;
return Err(RewriteError::SkipFormatting);
}
};
}
@ -596,7 +596,7 @@ pub(crate) fn trim_left_preserve_layout(
// just InString{Commented} in order to allow the start of a string to be indented
let new_veto_trim_value = (kind == FullCodeCharKind::InString
|| (config.version() == Version::Two
|| (config.style_edition() >= StyleEdition::Edition2024
&& kind == FullCodeCharKind::InStringCommented))
&& !line.ends_with('\\');
let line = if veto_trim || new_veto_trim_value {
@ -612,7 +612,7 @@ pub(crate) fn trim_left_preserve_layout(
// such lines should not be taken into account when computing the minimum.
match kind {
FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented
if config.version() == Version::Two =>
if config.style_edition() >= StyleEdition::Edition2024 =>
{
None
}
@ -656,7 +656,7 @@ pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Conf
// formatting the code block, therefore the string's indentation needs
// to be adjusted for the code surrounding the code block.
config.format_strings() && line.ends_with('\\')
} else if config.version() == Version::Two {
} else if config.style_edition() >= StyleEdition::Edition2024 {
!kind.is_commented_string()
} else {
true

View file

@ -11,9 +11,9 @@ use crate::config::lists::*;
use crate::expr::rewrite_field;
use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::rewrite::{Rewrite, RewriteContext, RewriteResult};
use crate::shape::{Indent, Shape};
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
@ -24,13 +24,13 @@ use crate::utils::{
pub(crate) trait AlignedItem {
fn skip(&self) -> bool;
fn get_span(&self) -> Span;
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult;
fn rewrite_aligned_item(
&self,
context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String>;
) -> RewriteResult;
}
impl AlignedItem for ast::FieldDef {
@ -42,24 +42,23 @@ impl AlignedItem for ast::FieldDef {
self.span()
}
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let attrs_str = self.attrs.rewrite(context, shape)?;
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let attrs_str = self.attrs.rewrite_result(context, shape)?;
let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo(), self.span.lo())
} else {
mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo())
};
let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str);
rewrite_struct_field_prefix(context, self).and_then(|field_str| {
combine_strs_with_missing_comments(
context,
&attrs_str,
&field_str,
missing_span,
shape,
attrs_extendable,
)
})
let field_str = rewrite_struct_field_prefix(context, self)?;
combine_strs_with_missing_comments(
context,
&attrs_str,
&field_str,
missing_span,
shape,
attrs_extendable,
)
}
fn rewrite_aligned_item(
@ -67,7 +66,7 @@ impl AlignedItem for ast::FieldDef {
context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String> {
) -> RewriteResult {
rewrite_struct_field(context, self, shape, prefix_max_width)
}
}
@ -81,8 +80,8 @@ impl AlignedItem for ast::ExprField {
self.span()
}
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let attrs_str = self.attrs.rewrite(context, shape)?;
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let attrs_str = self.attrs.rewrite_result(context, shape)?;
let name = rewrite_ident(context, self.ident);
let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo(), self.span.lo())
@ -104,7 +103,7 @@ impl AlignedItem for ast::ExprField {
context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String> {
) -> RewriteResult {
rewrite_field(context, self, shape, prefix_max_width)
}
}
@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width<T: AlignedItem>(
.rewrite_prefix(context, shape)
.map(|field_str| trimmed_last_line_width(&field_str))
})
.fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| {
.fold_ok((0, ::std::usize::MAX), |(max_len, min_len), len| {
(cmp::max(max_len, len), cmp::min(min_len, len))
})
.unwrap_or((0, 0))
@ -246,12 +245,12 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
if tactic == DefinitiveListTactic::Horizontal {
// since the items fits on a line, there is no need to align them
let do_rewrite =
|field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) };
|field: &T| -> RewriteResult { field.rewrite_aligned_item(context, item_shape, 0) };
fields
.iter()
.zip(items.iter_mut())
.for_each(|(field, list_item): (&T, &mut ListItem)| {
if list_item.item.is_some() {
if list_item.item.is_ok() {
list_item.item = do_rewrite(field);
}
});
@ -267,7 +266,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
.tactic(tactic)
.trailing_separator(separator_tactic)
.preserve_newline(true);
write_list(&items, &fmt)
write_list(&items, &fmt).ok()
}
/// Returns the index in `fields` up to which a field belongs to the current group.

View file

@ -3,24 +3,23 @@ use std::rc::Rc;
use rustc_ast::{ast, token::Delimiter, visit};
use rustc_data_structures::sync::Lrc;
use rustc_span::{symbol, BytePos, Pos, Span};
use rustc_span::{BytePos, Pos, Span, symbol};
use tracing::debug;
use crate::attr::*;
use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
use crate::config::Version;
use crate::config::{BraceStyle, Config, MacroSelector};
use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment};
use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition};
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,
rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts,
FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait,
format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, rewrite_type_alias,
};
use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition};
use crate::macros::{MacroPosition, macro_style, rewrite_macro, rewrite_macro_def};
use crate::modules::Module;
use crate::parse::session::ParseSess;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::{Indent, Shape};
use crate::skip::{is_skip_attr, SkipContext};
use crate::skip::{SkipContext, is_skip_attr};
use crate::source_map::{LineRangeUtils, SpanUtils};
use crate::spanned::Spanned;
use crate::stmt::Stmt;
@ -291,7 +290,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let mut comment_shape =
Shape::indented(self.block_indent, config).comment(config);
if self.config.version() == Version::Two && comment_on_same_line {
if self.config.style_edition() >= StyleEdition::Edition2024
&& comment_on_same_line
{
self.push_str(" ");
// put the first line of the comment on the same line as the
// block's last line
@ -312,8 +313,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let comment_str =
rewrite_comment(other_lines, false, comment_shape, config);
match comment_str {
Some(ref s) => self.push_str(s),
None => self.push_str(other_lines),
Ok(ref s) => self.push_str(s),
Err(_) => self.push_str(other_lines),
}
}
}
@ -342,8 +343,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config);
match comment_str {
Some(ref s) => self.push_str(s),
None => self.push_str(&sub_slice),
Ok(ref s) => self.push_str(s),
Err(_) => self.push_str(&sub_slice),
}
}
}
@ -561,9 +562,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
)
} else {
let indent = self.block_indent;
let rewrite = self.rewrite_required_fn(
indent, item.ident, sig, &item.vis, generics, item.span,
);
let rewrite = self
.rewrite_required_fn(
indent, item.ident, sig, &item.vis, generics, item.span,
)
.ok();
self.push_rewrite(item.span, rewrite);
}
}
@ -584,7 +587,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
item.ident,
&item.vis,
item.span,
);
)
.ok();
self.push_rewrite(item.span, rewrite);
}
ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => {
@ -609,7 +613,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
self.block_indent,
visitor_kind,
span,
);
)
.ok();
self.push_rewrite(span, rewrite);
}
@ -655,8 +660,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
);
} else {
let indent = self.block_indent;
let rewrite =
self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span);
let rewrite = self
.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span)
.ok();
self.push_rewrite(ai.span, rewrite);
}
}
@ -683,7 +689,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
// 1 = ;
let shape = self.shape().saturating_sub_width(1);
let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos).ok());
// As of v638 of the rustc-ap-* crates, the associated span no longer includes
// the trailing semicolon. This determines the correct span to ensure scenarios
// with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`)