// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::cmp;
-
-use syntax::{ast, visit};
use syntax::attr::HasAttrs;
use syntax::codemap::{self, BytePos, CodeMap, Pos, Span};
use syntax::parse::ParseSess;
+use syntax::{ast, visit};
-use expr::rewrite_literal;
-use spanned::Spanned;
+use attr::*;
use codemap::{LineRangeUtils, SpanUtils};
-use comment::{combine_strs_with_missing_comments, contains_comment, remove_trailing_white_spaces,
- CodeCharKind, CommentCodeSlices, FindUncommented};
-use comment::rewrite_comment;
+use comment::{CodeCharKind, CommentCodeSlices, FindUncommented};
use config::{BraceStyle, Config};
-use items::{format_impl, format_trait, format_trait_alias, rewrite_associated_impl_type,
- rewrite_associated_type, rewrite_type_alias, FnSig, StaticParts, StructParts};
-use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace,
- SeparatorTactic};
-use macros::{rewrite_macro, MacroPosition};
-use regex::Regex;
+use items::{
+ format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item,
+ rewrite_associated_impl_type, rewrite_associated_type, rewrite_existential_impl_type,
+ rewrite_existential_type, rewrite_extern_crate, rewrite_type_alias, FnSig, StaticParts,
+ StructParts,
+};
+use macros::{rewrite_macro, rewrite_macro_def, MacroPosition};
use rewrite::{Rewrite, RewriteContext};
use shape::{Indent, Shape};
-use utils::{self, contains_skip, count_newlines, inner_attributes, mk_sp, ptr_vec_to_ref_vec};
-
-fn is_use_item(item: &ast::Item) -> bool {
- match item.node {
- ast::ItemKind::Use(_) => true,
- _ => false,
- }
-}
+use spanned::Spanned;
+use utils::{
+ self, contains_skip, count_newlines, inner_attributes, mk_sp, ptr_vec_to_ref_vec,
+ rewrite_ident, DEPR_SKIP_ANNOTATION,
+};
+use {ErrorKind, FormatReport, FormattingError};
-fn is_extern_crate(item: &ast::Item) -> bool {
- match item.node {
- ast::ItemKind::ExternCrate(..) => true,
- _ => false,
- }
-}
+use std::cell::RefCell;
/// Creates a string slice corresponding to the specified span.
pub struct SnippetProvider<'a> {
pub snippet_provider: &'a SnippetProvider<'a>,
pub line_number: usize,
pub skipped_range: Vec<(usize, usize)>,
+ pub macro_rewrite_failure: bool,
+ pub(crate) report: FormatReport,
}
impl<'b, 'a: 'b> FmtVisitor<'a> {
match stmt.node {
ast::StmtKind::Item(ref item) => {
self.visit_item(item);
+ // Handle potential `;` after the item.
+ self.format_missing(stmt.span.hi());
}
ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
if contains_skip(get_attrs_from_stmt(stmt)) {
self.block_indent = self.block_indent.block_indent(self.config);
self.push_str("{");
- if self.config.remove_blank_lines_at_start_or_end_of_block() {
- if let Some(first_stmt) = b.stmts.first() {
- let attr_lo = inner_attrs
- .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo()))
- .or_else(|| {
- // Attributes for an item in a statement position
- // do not belong to the statement. (rust-lang/rust#34459)
- if let ast::StmtKind::Item(ref item) = first_stmt.node {
- item.attrs.first()
- } else {
- first_stmt.attrs().first()
- }.and_then(|attr| {
- // Some stmts can have embedded attributes.
- // e.g. `match { #![attr] ... }`
- let attr_lo = attr.span.lo();
- if attr_lo < first_stmt.span.lo() {
- Some(attr_lo)
- } else {
- None
- }
- })
- });
-
- let snippet = self.snippet(mk_sp(
- self.last_pos,
- attr_lo.unwrap_or(first_stmt.span.lo()),
- ));
- let len = CommentCodeSlices::new(snippet)
- .nth(0)
- .and_then(|(kind, _, s)| {
- if kind == CodeCharKind::Normal {
- s.rfind('\n')
+ if let Some(first_stmt) = b.stmts.first() {
+ let attr_lo = inner_attrs
+ .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo()))
+ .or_else(|| {
+ // Attributes for an item in a statement position
+ // do not belong to the statement. (rust-lang/rust#34459)
+ if let ast::StmtKind::Item(ref item) = first_stmt.node {
+ item.attrs.first()
+ } else {
+ first_stmt.attrs().first()
+ }.and_then(|attr| {
+ // Some stmts can have embedded attributes.
+ // e.g. `match { #![attr] ... }`
+ let attr_lo = attr.span.lo();
+ if attr_lo < first_stmt.span.lo() {
+ Some(attr_lo)
} else {
None
}
- });
- if let Some(len) = len {
- self.last_pos = self.last_pos + BytePos::from_usize(len);
- }
+ })
+ });
+
+ let snippet = self.snippet(mk_sp(
+ self.last_pos,
+ attr_lo.unwrap_or_else(|| first_stmt.span.lo()),
+ ));
+ let len = CommentCodeSlices::new(snippet)
+ .nth(0)
+ .and_then(|(kind, _, s)| {
+ if kind == CodeCharKind::Normal {
+ s.rfind('\n')
+ } else {
+ None
+ }
+ });
+ if let Some(len) = len {
+ self.last_pos = self.last_pos + BytePos::from_usize(len);
}
}
}
let mut remove_len = BytePos(0);
- if self.config.remove_blank_lines_at_start_or_end_of_block() {
- if let Some(stmt) = b.stmts.last() {
- let snippet = self.snippet(mk_sp(
- stmt.span.hi(),
- source!(self, b.span).hi() - brace_compensation,
- ));
- let len = CommentCodeSlices::new(snippet)
- .last()
- .and_then(|(kind, _, s)| {
- if kind == CodeCharKind::Normal && s.trim().is_empty() {
- Some(s.len())
- } else {
- None
- }
- });
- if let Some(len) = len {
- remove_len = BytePos::from_usize(len);
- }
+ if let Some(stmt) = b.stmts.last() {
+ let snippet = self.snippet(mk_sp(
+ stmt.span.hi(),
+ source!(self, b.span).hi() - brace_compensation,
+ ));
+ let len = CommentCodeSlices::new(snippet)
+ .last()
+ .and_then(|(kind, _, s)| {
+ if kind == CodeCharKind::Normal && s.trim().is_empty() {
+ Some(s.len())
+ } else {
+ None
+ }
+ });
+ if let Some(len) = len {
+ remove_len = BytePos::from_usize(len);
}
}
let indent = self.block_indent;
let block;
let rewrite = match fk {
- visit::FnKind::ItemFn(ident, _, _, _, _, b) | visit::FnKind::Method(ident, _, _, b) => {
+ visit::FnKind::ItemFn(ident, _, _, b) | visit::FnKind::Method(ident, _, _, b) => {
block = b;
self.rewrite_fn(
indent,
&FnSig::from_fn_kind(&fk, generics, fd, defaultness),
mk_sp(s.lo(), b.span.lo()),
b,
+ inner_attrs,
)
}
visit::FnKind::Closure(_) => unreachable!(),
let filtered_attrs;
let mut attrs = &item.attrs;
match item.node {
- ast::ItemKind::Mod(ref m) => {
- let outer_file = self.codemap.lookup_char_pos(item.span.lo()).file;
- let inner_file = self.codemap.lookup_char_pos(m.inner.lo()).file;
- if outer_file.name == inner_file.name {
- // Module is inline, in this case we treat modules like any
- // other item.
- if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
- self.push_skipped_with_span(item.span());
- return;
- }
- } else if contains_skip(&item.attrs) {
- // Module is not inline, but should be skipped.
+ // For use items, skip rewriting attributes. Just check for a skip attribute.
+ ast::ItemKind::Use(..) => {
+ if contains_skip(attrs) {
+ self.push_skipped_with_span(item.span());
+ return;
+ }
+ }
+ // Module is inline, in this case we treat it like any other item.
+ _ if !is_mod_decl(item) => {
+ if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
+ self.push_skipped_with_span(item.span());
return;
- } else {
- // Module is not inline and should not be skipped. We want
- // to process only the attributes in the current file.
- filtered_attrs = item.attrs
- .iter()
- .filter_map(|a| {
- let attr_file = self.codemap.lookup_char_pos(a.span.lo()).file;
- if attr_file.name == outer_file.name {
- Some(a.clone())
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
- // Assert because if we should skip it should be caught by
- // the above case.
- assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
- attrs = &filtered_attrs;
}
}
+ // Module is not inline, but should be skipped.
+ ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => {
+ return;
+ }
+ // Module is not inline and should not be skipped. We want
+ // to process only the attributes in the current file.
+ ast::ItemKind::Mod(..) => {
+ filtered_attrs = filter_inline_attrs(&item.attrs, item.span());
+ // Assert because if we should skip it should be caught by
+ // the above case.
+ assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
+ attrs = &filtered_attrs;
+ }
_ => {
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
self.push_skipped_with_span(item.span());
let snippet = self.snippet(item.span);
let where_span_end = snippet
.find_uncommented("{")
- .map(|x| (BytePos(x as u32)) + source!(self, item.span).lo());
+ .map(|x| BytePos(x as u32) + source!(self, item.span).lo());
let rw = format_impl(&self.get_context(), item, self.block_indent, where_span_end);
self.push_rewrite(item.span, rw);
}
let rw = format_trait(&self.get_context(), item, self.block_indent);
self.push_rewrite(item.span, rw);
}
- ast::ItemKind::TraitAlias(ref generics, ref ty_param_bounds) => {
+ ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => {
let shape = Shape::indented(self.block_indent, self.config);
let rw = format_trait_alias(
&self.get_context(),
item.ident,
generics,
- ty_param_bounds,
+ generic_bounds,
shape,
);
self.push_rewrite(item.span, rw);
self.last_pos = source!(self, item.span).hi();
}
ast::ItemKind::Mod(ref module) => {
+ let is_inline = !is_mod_decl(item);
self.format_missing_with_indent(source!(self, item.span).lo());
- self.format_mod(module, &item.vis, item.span, item.ident, attrs);
+ self.format_mod(module, &item.vis, item.span, item.ident, attrs, is_inline);
}
ast::ItemKind::Mac(ref mac) => {
self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => {
self.visit_static(&StaticParts::from_item(item));
}
- ast::ItemKind::AutoImpl(..) => {
- // FIXME(#78): format impl definitions.
- }
- ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
+ ast::ItemKind::Fn(ref decl, fn_header, ref generics, ref body) => {
+ let inner_attrs = inner_attributes(&item.attrs);
self.visit_fn(
- visit::FnKind::ItemFn(item.ident, unsafety, constness, abi, &item.vis, body),
+ visit::FnKind::ItemFn(item.ident, fn_header, &item.vis, body),
generics,
decl,
item.span,
ast::Defaultness::Final,
- Some(&item.attrs),
+ Some(&inner_attrs),
)
}
ast::ItemKind::Ty(ref ty, ref generics) => {
ty,
generics,
&item.vis,
- item.span,
+ );
+ self.push_rewrite(item.span, rewrite);
+ }
+ ast::ItemKind::Existential(ref generic_bounds, ref generics) => {
+ let rewrite = rewrite_existential_type(
+ &self.get_context(),
+ self.block_indent,
+ item.ident,
+ generic_bounds,
+ generics,
+ &item.vis,
);
self.push_rewrite(item.span, rewrite);
}
let snippet = Some(self.snippet(item.span).to_owned());
self.push_rewrite(item.span, snippet);
}
- ast::ItemKind::MacroDef(..) => {
- // FIXME(#1539): macros 2.0
- let mac_snippet = Some(remove_trailing_white_spaces(self.snippet(item.span)));
- self.push_rewrite(item.span, mac_snippet);
+ ast::ItemKind::MacroDef(ref def) => {
+ let rewrite = rewrite_macro_def(
+ &self.get_context(),
+ self.shape(),
+ self.block_indent,
+ def,
+ item.ident,
+ &item.vis,
+ item.span,
+ );
+ self.push_rewrite(item.span, rewrite);
}
}
}
self.push_rewrite(ti.span, rewrite);
}
ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
+ let inner_attrs = inner_attributes(&ti.attrs);
self.visit_fn(
visit::FnKind::Method(ti.ident, sig, None, body),
&ti.generics,
&sig.decl,
ti.span,
ast::Defaultness::Final,
- Some(&ti.attrs),
+ Some(&inner_attrs),
);
}
- ast::TraitItemKind::Type(ref type_param_bounds, ref type_default) => {
+ ast::TraitItemKind::Type(ref generic_bounds, ref type_default) => {
let rewrite = rewrite_associated_type(
ti.ident,
type_default.as_ref(),
- Some(type_param_bounds),
+ Some(generic_bounds),
&self.get_context(),
self.block_indent,
);
match ii.node {
ast::ImplItemKind::Method(ref sig, ref body) => {
+ let inner_attrs = inner_attributes(&ii.attrs);
self.visit_fn(
visit::FnKind::Method(ii.ident, sig, Some(&ii.vis), body),
&ii.generics,
&sig.decl,
ii.span,
ii.defaultness,
- Some(&ii.attrs),
+ Some(&inner_attrs),
);
}
ast::ImplItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)),
ii.ident,
ii.defaultness,
Some(ty),
- None,
&self.get_context(),
self.block_indent,
);
self.push_rewrite(ii.span, rewrite);
}
+ ast::ImplItemKind::Existential(ref generic_bounds) => {
+ let rewrite = rewrite_existential_impl_type(
+ &self.get_context(),
+ ii.ident,
+ generic_bounds,
+ self.block_indent,
+ );
+ self.push_rewrite(ii.span, rewrite);
+ }
ast::ImplItemKind::Macro(ref mac) => {
self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
}
// 1 = ;
let shape = self.shape().sub_width(1).unwrap();
- let rewrite = rewrite_macro(mac, ident, &self.get_context(), shape, pos);
+ let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
self.push_rewrite(mac.span, rewrite);
}
self.buffer.push_str(s);
}
+ #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) {
if let Some(ref s) = rewrite {
self.push_str(s);
}
pub fn from_context(ctx: &'a RewriteContext) -> FmtVisitor<'a> {
- FmtVisitor::from_codemap(ctx.parse_session, ctx.config, ctx.snippet_provider)
+ FmtVisitor::from_codemap(
+ ctx.parse_session,
+ ctx.config,
+ ctx.snippet_provider,
+ ctx.report.clone(),
+ )
}
- pub fn from_codemap(
+ pub(crate) fn from_codemap(
parse_session: &'a ParseSess,
config: &'a Config,
snippet_provider: &'a SnippetProvider,
+ report: FormatReport,
) -> FmtVisitor<'a> {
FmtVisitor {
- parse_session: parse_session,
+ parse_session,
codemap: parse_session.codemap(),
buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
last_pos: BytePos(0),
block_indent: Indent::empty(),
- config: config,
+ config,
is_if_else_block: false,
- snippet_provider: snippet_provider,
+ snippet_provider,
line_number: 0,
skipped_range: vec![],
+ macro_rewrite_failure: false,
+ report,
}
}
// Returns true if we should skip the following item.
pub fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
+ for attr in attrs {
+ if attr.name() == DEPR_SKIP_ANNOTATION {
+ let file_name = self.codemap.span_to_filename(attr.span).into();
+ self.report.append(
+ file_name,
+ vec![FormattingError::from_span(
+ &attr.span,
+ &self.codemap,
+ ErrorKind::DeprecatedAttr,
+ )],
+ );
+ } else if attr.path.segments[0].ident.to_string() == "rustfmt" {
+ if attr.path.segments.len() == 1
+ || attr.path.segments[1].ident.to_string() != "skip"
+ {
+ let file_name = self.codemap.span_to_filename(attr.span).into();
+ self.report.append(
+ file_name,
+ vec![FormattingError::from_span(
+ &attr.span,
+ &self.codemap,
+ ErrorKind::BadAttr,
+ )],
+ );
+ }
+ }
+ }
if contains_skip(attrs) {
return true;
}
false
}
- fn reorder_items<F>(&mut self, items_left: &[&ast::Item], is_item: &F, in_group: bool) -> usize
- where
- F: Fn(&ast::Item) -> bool,
- {
- let mut last = self.codemap.lookup_line_range(items_left[0].span());
- let item_length = items_left
- .iter()
- .take_while(|ppi| {
- is_item(&***ppi) && (!in_group || {
- let current = self.codemap.lookup_line_range(ppi.span());
- let in_same_group = current.lo < last.hi + 2;
- last = current;
- in_same_group
- })
- })
- .count();
- let items = &items_left[..item_length];
-
- let at_least_one_in_file_lines = items
- .iter()
- .any(|item| !out_of_file_lines_range!(self, item.span));
-
- if at_least_one_in_file_lines {
- self.format_imports(items);
- } else {
- for item in items {
- self.push_rewrite(item.span, None);
- }
- }
-
- item_length
- }
-
- fn walk_items(&mut self, mut items_left: &[&ast::Item]) {
- while !items_left.is_empty() {
- // If the next item is a `use` declaration, then extract it and any subsequent `use`s
- // to be potentially reordered within `format_imports`. Otherwise, just format the
- // next item for output.
- if self.config.reorder_imports() && is_use_item(&*items_left[0]) {
- let used_items_len = self.reorder_items(
- items_left,
- &is_use_item,
- self.config.reorder_imports_in_group(),
- );
- let (_, rest) = items_left.split_at(used_items_len);
- items_left = rest;
- } else if self.config.reorder_extern_crates() && is_extern_crate(&*items_left[0]) {
- let used_items_len = self.reorder_items(
- items_left,
- &is_extern_crate,
- self.config.reorder_extern_crates_in_group(),
- );
- let (_, rest) = items_left.split_at(used_items_len);
- items_left = rest;
- } else {
- // `unwrap()` is safe here because we know `items_left`
- // has elements from the loop condition
- let (item, rest) = items_left.split_first().unwrap();
- self.visit_item(item);
- items_left = rest;
- }
- }
- }
-
fn walk_mod_items(&mut self, m: &ast::Mod) {
- self.walk_items(&ptr_vec_to_ref_vec(&m.items));
+ self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&m.items));
}
fn walk_stmts(&mut self, stmts: &[ast::Stmt]) {
self.visit_stmt(&stmts[0]);
self.walk_stmts(&stmts[1..]);
} else {
- self.walk_items(&items);
+ self.visit_items_with_reordering(&items);
self.walk_stmts(&stmts[items.len()..]);
}
}
s: Span,
ident: ast::Ident,
attrs: &[ast::Attribute],
+ is_internal: bool,
) {
- // Decide whether this is an inline mod or an external mod.
- let local_file_name = self.codemap.span_to_filename(s);
- let inner_span = source!(self, m.inner);
- let is_internal = !(inner_span.lo().0 == 0 && inner_span.hi().0 == 0)
- && local_file_name == self.codemap.span_to_filename(inner_span);
-
- self.push_str(&*utils::format_visibility(vis));
+ let vis_str = utils::format_visibility(&self.get_context(), vis);
+ self.push_str(&*vis_str);
self.push_str("mod ");
- self.push_str(&ident.to_string());
+ // Calling `to_owned()` to work around borrow checker.
+ let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
+ self.push_str(&ident_str);
if is_internal {
match self.config.brace_style() {
BraceStyle::AlwaysNextLine => {
- let sep_str = format!("\n{}{{", self.block_indent.to_string(self.config));
- self.push_str(&sep_str);
+ let indent_str = self.block_indent.to_string_with_newline(self.config);
+ self.push_str(&indent_str);
+ self.push_str("{");
}
_ => self.push_str(" {"),
}
// Hackery to account for the closing }.
- let mod_lo = self.codemap.span_after(source!(self, s), "{");
+ let mod_lo = self.snippet_provider.span_after(source!(self, s), "{");
let body_snippet =
self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi() - BytePos(1)));
let body_snippet = body_snippet.trim();
}
pub fn skip_empty_lines(&mut self, end_pos: BytePos) {
- while let Some(pos) = self.codemap
+ while let Some(pos) = self
+ .snippet_provider
.opt_span_after(mk_sp(self.last_pos, end_pos), "\n")
{
if let Some(snippet) = self.opt_snippet(mk_sp(self.last_pos, pos)) {
}
}
+ pub fn with_context<F>(&mut self, f: F) -> Option<String>
+ where
+ F: Fn(&RewriteContext) -> Option<String>,
+ {
+ let result;
+ let macro_rewrite_failure = {
+ let context = self.get_context();
+ result = f(&context);
+ unsafe { *context.macro_rewrite_failure.as_ptr() }
+ };
+ self.macro_rewrite_failure |= macro_rewrite_failure;
+ result
+ }
+
pub fn get_context(&self) -> RewriteContext {
RewriteContext {
parse_session: self.parse_session,
codemap: self.codemap,
config: self.config,
- inside_macro: false,
- use_block: false,
- is_if_else_block: false,
- force_one_line_chain: false,
+ inside_macro: RefCell::new(false),
+ use_block: RefCell::new(false),
+ is_if_else_block: RefCell::new(false),
+ force_one_line_chain: RefCell::new(false),
snippet_provider: self.snippet_provider,
+ macro_rewrite_failure: RefCell::new(false),
+ report: self.report.clone(),
}
}
}
-
-impl Rewrite for ast::NestedMetaItem {
- fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
- match self.node {
- ast::NestedMetaItemKind::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
- ast::NestedMetaItemKind::Literal(ref l) => rewrite_literal(context, l, shape),
- }
- }
-}
-
-impl Rewrite for ast::MetaItem {
- fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
- Some(match self.node {
- ast::MetaItemKind::Word => String::from(&*self.name.as_str()),
- ast::MetaItemKind::List(ref list) => {
- let name = self.name.as_str();
- // 1 = `(`, 2 = `]` and `)`
- let item_shape = shape
- .visual_indent(0)
- .shrink_left(name.len() + 1)
- .and_then(|s| s.sub_width(2))?;
- let items = itemize_list(
- context.codemap,
- list.iter(),
- ")",
- ",",
- |nested_meta_item| nested_meta_item.span.lo(),
- |nested_meta_item| nested_meta_item.span.hi(),
- |nested_meta_item| nested_meta_item.rewrite(context, item_shape),
- self.span.lo(),
- self.span.hi(),
- false,
- );
- let item_vec = items.collect::<Vec<_>>();
- let fmt = ListFormatting {
- tactic: DefinitiveListTactic::Mixed,
- separator: ",",
- trailing_separator: SeparatorTactic::Never,
- separator_place: SeparatorPlace::Back,
- shape: item_shape,
- ends_with_newline: false,
- preserve_newline: false,
- config: context.config,
- };
- format!("{}({})", name, write_list(&item_vec, &fmt)?)
- }
- ast::MetaItemKind::NameValue(ref literal) => {
- let name = self.name.as_str();
- // 3 = ` = `
- let lit_shape = shape.shrink_left(name.len() + 3)?;
- let value = rewrite_literal(context, literal, lit_shape)?;
- format!("{} = {}", name, value)
- }
- })
- }
-}
-
-impl Rewrite for ast::Attribute {
- fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
- let prefix = match self.style {
- ast::AttrStyle::Inner => "#!",
- ast::AttrStyle::Outer => "#",
- };
- let snippet = context.snippet(self.span);
- if self.is_sugared_doc {
- let doc_shape = Shape {
- width: cmp::min(shape.width, context.config.comment_width())
- .checked_sub(shape.indent.width())
- .unwrap_or(0),
- ..shape
- };
- rewrite_comment(snippet, false, doc_shape, context.config)
- } else {
- if contains_comment(snippet) {
- return Some(snippet.to_owned());
- }
- // 1 = `[`
- let shape = shape.offset_left(prefix.len() + 1)?;
- self.meta()?
- .rewrite(context, shape)
- .map(|rw| format!("{}[{}]", prefix, rw))
- }
- }
-}
-
-/// Returns the first group of attributes that fills the given predicate.
-/// We consider two doc comments are in different group if they are separated by normal comments.
-fn take_while_with_pred<'a, P>(
- context: &RewriteContext,
- attrs: &'a [ast::Attribute],
- pred: P,
-) -> &'a [ast::Attribute]
-where
- P: Fn(&ast::Attribute) -> bool,
-{
- let mut last_index = 0;
- let mut iter = attrs.iter().enumerate().peekable();
- while let Some((i, attr)) = iter.next() {
- if !pred(attr) {
- break;
- }
- if let Some(&(_, next_attr)) = iter.peek() {
- // Extract comments between two attributes.
- let span_between_attr = mk_sp(attr.span.hi(), next_attr.span.lo());
- let snippet = context.snippet(span_between_attr);
- if count_newlines(snippet) >= 2 || snippet.contains('/') {
- break;
- }
- }
- last_index = i;
- }
- if last_index == 0 {
- &[]
- } else {
- &attrs[..last_index + 1]
- }
-}
-
-fn rewrite_first_group_attrs(
- context: &RewriteContext,
- attrs: &[ast::Attribute],
- shape: Shape,
-) -> Option<(usize, String)> {
- if attrs.is_empty() {
- return Some((0, String::new()));
- }
- // Rewrite doc comments
- let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_sugared_doc);
- if !sugared_docs.is_empty() {
- let snippet = sugared_docs
- .iter()
- .map(|a| context.snippet(a.span))
- .collect::<Vec<_>>()
- .join("\n");
- return Some((
- sugared_docs.len(),
- rewrite_comment(&snippet, false, shape, context.config)?,
- ));
- }
- // Rewrite `#[derive(..)]`s.
- if context.config.merge_derives() {
- let derives = take_while_with_pred(context, attrs, is_derive);
- if !derives.is_empty() {
- let mut derive_args = vec![];
- for derive in derives {
- derive_args.append(&mut get_derive_args(context, derive)?);
- }
- return Some((derives.len(), format_derive(context, &derive_args, shape)?));
- }
- }
- // Rewrite the first attribute.
- Some((1, attrs[0].rewrite(context, shape)?))
-}
-
-fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
- // Look at before and after comment and see if there are any empty lines.
- let comment_begin = comment.chars().position(|c| c == '/');
- let len = comment_begin.unwrap_or_else(|| comment.len());
- let mlb = count_newlines(&comment[..len]) > 1;
- let mla = if comment_begin.is_none() {
- mlb
- } else {
- let comment_end = comment.chars().rev().position(|c| !c.is_whitespace());
- let len = comment_end.unwrap();
- comment
- .chars()
- .rev()
- .take(len)
- .filter(|c| *c == '\n')
- .count() > 1
- };
- (if mlb { "\n" } else { "" }, if mla { "\n" } else { "" })
-}
-
-impl<'a> Rewrite for [ast::Attribute] {
- fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
- if self.is_empty() {
- return Some(String::new());
- }
- let (first_group_len, first_group_str) = rewrite_first_group_attrs(context, self, shape)?;
- if self.len() == 1 || first_group_len == self.len() {
- Some(first_group_str)
- } else {
- let rest_str = self[first_group_len..].rewrite(context, shape)?;
- let missing_span = mk_sp(
- self[first_group_len - 1].span.hi(),
- self[first_group_len].span.lo(),
- );
- // Preserve an empty line before/after doc comments.
- if self[0].is_sugared_doc || self[first_group_len].is_sugared_doc {
- let snippet = context.snippet(missing_span);
- let (mla, mlb) = has_newlines_before_after_comment(snippet);
- let comment = ::comment::recover_missing_comment_in_span(
- missing_span,
- shape.with_max_width(context.config),
- context,
- 0,
- )?;
- let comment = if comment.is_empty() {
- format!("\n{}", mlb)
- } else {
- format!("{}{}\n{}", mla, comment, mlb)
- };
- Some(format!(
- "{}{}{}{}",
- first_group_str,
- comment,
- shape.indent.to_string(context.config),
- rest_str
- ))
- } else {
- combine_strs_with_missing_comments(
- context,
- &first_group_str,
- &rest_str,
- missing_span,
- shape,
- false,
- )
- }
- }
- }
-}
-
-// Format `#[derive(..)]`, using visual indent & mixed style when we need to go multiline.
-fn format_derive(context: &RewriteContext, derive_args: &[&str], shape: Shape) -> Option<String> {
- let mut result = String::with_capacity(128);
- result.push_str("#[derive(");
- // 11 = `#[derive()]`
- let initial_budget = shape.width.checked_sub(11)?;
- let mut budget = initial_budget;
- let num = derive_args.len();
- for (i, a) in derive_args.iter().enumerate() {
- // 2 = `, ` or `)]`
- let width = a.len() + 2;
- if width > budget {
- if i > 0 {
- // Remove trailing whitespace.
- result.pop();
- }
- result.push('\n');
- // 9 = `#[derive(`
- result.push_str(&(shape.indent + 9).to_string(context.config));
- budget = initial_budget;
- } else {
- budget = budget.checked_sub(width).unwrap_or(0);
- }
- result.push_str(a);
- if i != num - 1 {
- result.push_str(", ")
- }
- }
- result.push_str(")]");
- Some(result)
-}
-
-fn is_derive(attr: &ast::Attribute) -> bool {
- match attr.meta() {
- Some(meta_item) => match meta_item.node {
- ast::MetaItemKind::List(..) => meta_item.name.as_str() == "derive",
- _ => false,
- },
- _ => false,
- }
-}
-
-/// Returns the arguments of `#[derive(...)]`.
-fn get_derive_args<'a>(context: &'a RewriteContext, attr: &ast::Attribute) -> Option<Vec<&'a str>> {
- attr.meta().and_then(|meta_item| match meta_item.node {
- ast::MetaItemKind::List(ref args) if meta_item.name.as_str() == "derive" => {
- // Every argument of `derive` should be `NestedMetaItemKind::Literal`.
- Some(
- args.iter()
- .map(|a| context.snippet(a.span))
- .collect::<Vec<_>>(),
- )
- }
- _ => None,
- })
-}
-
-// Rewrite `extern crate foo;` WITHOUT attributes.
-pub fn rewrite_extern_crate(context: &RewriteContext, item: &ast::Item) -> Option<String> {
- assert!(is_extern_crate(item));
- let new_str = context.snippet(item.span);
- Some(if contains_comment(new_str) {
- new_str.to_owned()
- } else {
- let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
- String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"))
- })
-}
-
-fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] {
- match stmt.node {
- ast::StmtKind::Local(ref local) => &local.attrs,
- ast::StmtKind::Item(ref item) => &item.attrs,
- ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => &expr.attrs,
- ast::StmtKind::Mac(ref mac) => &mac.2,
- }
-}