2016-05-28 00:58:25 +02:00
|
|
|
use std::borrow::Cow;
|
2015-09-07 21:34:37 +02:00
|
|
|
|
2018-10-13 17:39:19 -03:00
|
|
|
use bytecount;
|
|
|
|
|
2018-04-28 15:08:58 +08:00
|
|
|
use rustc_target::spec::abi;
|
2018-04-29 20:22:48 +08:00
|
|
|
use syntax::ast::{
|
2018-12-08 14:39:52 +09:00
|
|
|
self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind,
|
|
|
|
NodeId, Path, Visibility, VisibilityKind,
|
2018-04-29 20:22:48 +08:00
|
|
|
};
|
2018-04-28 15:08:58 +08:00
|
|
|
use syntax::ptr;
|
2018-08-23 17:14:19 -04:00
|
|
|
use syntax::source_map::{BytePos, Span, NO_EXPANSION};
|
2018-12-08 14:39:52 +09:00
|
|
|
use syntax_pos::Mark;
|
2019-01-15 08:41:09 +09:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
|
2019-02-04 13:30:43 +03:00
|
|
|
use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses};
|
|
|
|
use crate::config::{Config, Version};
|
|
|
|
use crate::rewrite::RewriteContext;
|
|
|
|
use crate::shape::{Indent, Shape};
|
|
|
|
|
2018-05-14 18:01:53 +12:00
|
|
|
pub const DEPR_SKIP_ANNOTATION: &str = "rustfmt_skip";
|
|
|
|
pub const SKIP_ANNOTATION: &str = "rustfmt::skip";
|
2015-06-05 16:56:59 +02:00
|
|
|
|
2019-02-09 16:14:30 +09:00
|
|
|
pub fn rewrite_ident<'a>(context: &'a RewriteContext<'_>, ident: ast::Ident) -> &'a str {
|
2018-06-25 23:36:45 +09:00
|
|
|
context.snippet(ident.span)
|
|
|
|
}
|
|
|
|
|
2016-03-07 13:41:32 -05:00
|
|
|
// Computes the length of a string's last line, minus offset.
|
2017-02-21 14:43:43 +13:00
|
|
|
pub fn extra_offset(text: &str, shape: Shape) -> usize {
|
2016-03-07 13:41:32 -05:00
|
|
|
match text.rfind('\n') {
|
|
|
|
// 1 for newline character
|
2018-05-14 21:58:57 +09:00
|
|
|
Some(idx) => text.len().saturating_sub(idx + 1 + shape.used_width()),
|
2016-03-07 13:41:32 -05:00
|
|
|
None => text.len(),
|
|
|
|
}
|
2015-12-27 14:25:37 +01:00
|
|
|
}
|
|
|
|
|
2018-07-20 16:18:45 +09:00
|
|
|
pub fn is_same_visibility(a: &Visibility, b: &Visibility) -> bool {
|
|
|
|
match (&a.node, &b.node) {
|
|
|
|
(
|
|
|
|
VisibilityKind::Restricted { path: p, .. },
|
|
|
|
VisibilityKind::Restricted { path: q, .. },
|
2018-10-15 20:36:39 +09:00
|
|
|
) => p.to_string() == q.to_string(),
|
2018-07-20 16:18:45 +09:00
|
|
|
(VisibilityKind::Public, VisibilityKind::Public)
|
|
|
|
| (VisibilityKind::Inherited, VisibilityKind::Inherited)
|
|
|
|
| (
|
|
|
|
VisibilityKind::Crate(CrateSugar::PubCrate),
|
|
|
|
VisibilityKind::Crate(CrateSugar::PubCrate),
|
|
|
|
)
|
|
|
|
| (
|
|
|
|
VisibilityKind::Crate(CrateSugar::JustCrate),
|
|
|
|
VisibilityKind::Crate(CrateSugar::JustCrate),
|
|
|
|
) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-28 00:58:25 +02:00
|
|
|
// Uses Cow to avoid allocating in the common cases.
|
2019-02-09 16:14:30 +09:00
|
|
|
pub fn format_visibility(context: &RewriteContext<'_>, vis: &Visibility) -> Cow<'static, str> {
|
2018-03-06 19:47:28 +09:00
|
|
|
match vis.node {
|
|
|
|
VisibilityKind::Public => Cow::from("pub "),
|
|
|
|
VisibilityKind::Inherited => Cow::from(""),
|
|
|
|
VisibilityKind::Crate(CrateSugar::PubCrate) => Cow::from("pub(crate) "),
|
|
|
|
VisibilityKind::Crate(CrateSugar::JustCrate) => Cow::from("crate "),
|
|
|
|
VisibilityKind::Restricted { ref path, .. } => {
|
2017-01-19 10:47:07 +13:00
|
|
|
let Path { ref segments, .. } = **path;
|
2018-06-25 23:36:45 +09:00
|
|
|
let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident));
|
2017-01-19 10:47:07 +13:00
|
|
|
if path.is_global() {
|
2017-06-16 08:49:49 +09:00
|
|
|
segments_iter
|
|
|
|
.next()
|
|
|
|
.expect("Non-global path in pub(restricted)?");
|
2017-01-19 10:47:07 +13:00
|
|
|
}
|
2017-05-12 22:25:26 +09:00
|
|
|
let is_keyword = |s: &str| s == "self" || s == "super";
|
2017-05-17 18:57:18 +12:00
|
|
|
let path = segments_iter.collect::<Vec<_>>().join("::");
|
2017-05-12 22:25:26 +09:00
|
|
|
let in_str = if is_keyword(&path) { "" } else { "in " };
|
2016-05-28 00:58:25 +02:00
|
|
|
|
2017-05-12 22:25:26 +09:00
|
|
|
Cow::from(format!("pub({}{}) ", in_str, path))
|
2016-05-28 00:58:25 +02:00
|
|
|
}
|
2015-05-29 12:41:26 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-08 19:40:22 +02:00
|
|
|
|
2018-07-29 07:37:24 -07:00
|
|
|
#[inline]
|
2018-07-29 17:20:11 -07:00
|
|
|
pub fn format_async(is_async: ast::IsAsync) -> &'static str {
|
|
|
|
match is_async {
|
2018-07-29 07:37:24 -07:00
|
|
|
ast::IsAsync::Async { .. } => "async ",
|
|
|
|
ast::IsAsync::NotAsync => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 22:19:20 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn format_constness(constness: ast::Constness) -> &'static str {
|
|
|
|
match constness {
|
|
|
|
ast::Constness::Const => "const ",
|
|
|
|
ast::Constness::NotConst => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 09:43:35 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn format_defaultness(defaultness: ast::Defaultness) -> &'static str {
|
|
|
|
match defaultness {
|
|
|
|
ast::Defaultness::Default => "default ",
|
|
|
|
ast::Defaultness::Final => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-22 16:07:38 +01:00
|
|
|
#[inline]
|
|
|
|
pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
|
|
|
|
match unsafety {
|
|
|
|
ast::Unsafety::Unsafe => "unsafe ",
|
|
|
|
ast::Unsafety::Normal => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 17:31:31 +03:00
|
|
|
#[inline]
|
|
|
|
pub fn format_auto(is_auto: ast::IsAuto) -> &'static str {
|
|
|
|
match is_auto {
|
|
|
|
ast::IsAuto::Yes => "auto ",
|
|
|
|
ast::IsAuto::No => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-24 19:54:38 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
|
|
|
|
match mutability {
|
2016-03-01 17:27:19 -05:00
|
|
|
ast::Mutability::Mutable => "mut ",
|
|
|
|
ast::Mutability::Immutable => "",
|
2015-07-24 19:54:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-22 16:07:38 +01:00
|
|
|
#[inline]
|
2017-09-29 15:08:48 +09:00
|
|
|
pub fn format_abi(abi: abi::Abi, explicit_abi: bool, is_mod: bool) -> Cow<'static, str> {
|
|
|
|
if abi == abi::Abi::Rust && !is_mod {
|
|
|
|
Cow::from("")
|
|
|
|
} else if abi == abi::Abi::C && !explicit_abi {
|
2017-09-15 18:11:24 +09:00
|
|
|
Cow::from("extern ")
|
2016-04-18 18:39:40 +02:00
|
|
|
} else {
|
2017-09-15 18:11:24 +09:00
|
|
|
Cow::from(format!("extern {} ", abi))
|
2016-04-18 18:39:40 +02:00
|
|
|
}
|
2015-11-22 16:07:38 +01:00
|
|
|
}
|
|
|
|
|
2017-09-15 22:27:20 +09:00
|
|
|
#[inline]
|
|
|
|
// Transform `Vec<syntax::ptr::P<T>>` into `Vec<&T>`
|
|
|
|
pub fn ptr_vec_to_ref_vec<T>(vec: &[ptr::P<T>]) -> Vec<&T> {
|
|
|
|
vec.iter().map(|x| &**x).collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
2017-08-05 15:21:46 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn filter_attributes(attrs: &[ast::Attribute], style: ast::AttrStyle) -> Vec<ast::Attribute> {
|
|
|
|
attrs
|
|
|
|
.iter()
|
|
|
|
.filter(|a| a.style == style)
|
|
|
|
.cloned()
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn inner_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
|
|
|
|
filter_attributes(attrs, ast::AttrStyle::Inner)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
|
|
|
|
filter_attributes(attrs, ast::AttrStyle::Outer)
|
|
|
|
}
|
|
|
|
|
2018-04-28 13:59:03 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn is_single_line(s: &str) -> bool {
|
|
|
|
s.chars().find(|&c| c == '\n').is_none()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn first_line_contains_single_line_comment(s: &str) -> bool {
|
|
|
|
s.lines().next().map_or(false, |l| l.contains("//"))
|
|
|
|
}
|
|
|
|
|
2017-08-11 17:52:13 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn last_line_contains_single_line_comment(s: &str) -> bool {
|
|
|
|
s.lines().last().map_or(false, |l| l.contains("//"))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_attributes_extendable(attrs_str: &str) -> bool {
|
2017-08-29 22:16:04 +09:00
|
|
|
!attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str)
|
2017-08-11 17:52:13 +09:00
|
|
|
}
|
|
|
|
|
2015-08-14 20:00:22 +12:00
|
|
|
// The width of the first line in s.
|
|
|
|
#[inline]
|
|
|
|
pub fn first_line_width(s: &str) -> usize {
|
2019-01-15 08:41:09 +09:00
|
|
|
unicode_str_width(s.splitn(2, '\n').next().unwrap_or(""))
|
2015-08-14 20:00:22 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
// The width of the last line in s.
|
|
|
|
#[inline]
|
|
|
|
pub fn last_line_width(s: &str) -> usize {
|
2019-01-15 08:41:09 +09:00
|
|
|
unicode_str_width(s.rsplitn(2, '\n').next().unwrap_or(""))
|
2015-08-14 20:00:22 +12:00
|
|
|
}
|
2017-06-15 16:26:41 +09:00
|
|
|
|
2017-08-01 22:19:20 +09:00
|
|
|
// The total used width of the last line.
|
|
|
|
#[inline]
|
|
|
|
pub fn last_line_used_width(s: &str, offset: usize) -> usize {
|
|
|
|
if s.contains('\n') {
|
|
|
|
last_line_width(s)
|
|
|
|
} else {
|
2019-01-15 08:41:09 +09:00
|
|
|
offset + unicode_str_width(s)
|
2017-08-01 22:19:20 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-12 10:30:57 +12:00
|
|
|
#[inline]
|
|
|
|
pub fn trimmed_last_line_width(s: &str) -> usize {
|
2019-01-15 08:41:09 +09:00
|
|
|
unicode_str_width(match s.rfind('\n') {
|
|
|
|
Some(n) => s[(n + 1)..].trim(),
|
|
|
|
None => s.trim(),
|
|
|
|
})
|
2016-04-12 10:30:57 +12:00
|
|
|
}
|
2015-08-14 20:00:22 +12:00
|
|
|
|
2017-06-17 21:11:55 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn last_line_extendable(s: &str) -> bool {
|
2017-10-07 22:17:01 +09:00
|
|
|
if s.ends_with("\"#") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for c in s.chars().rev() {
|
|
|
|
match c {
|
2018-02-06 09:36:29 +09:00
|
|
|
'(' | ')' | ']' | '}' | '?' | '>' => continue,
|
2017-10-07 22:17:01 +09:00
|
|
|
'\n' => break,
|
|
|
|
_ if c.is_whitespace() => continue,
|
|
|
|
_ => return false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
2017-06-17 21:11:55 +09:00
|
|
|
}
|
|
|
|
|
2015-08-14 20:00:22 +12:00
|
|
|
#[inline]
|
2015-06-23 15:58:58 +02:00
|
|
|
fn is_skip(meta_item: &MetaItem) -> bool {
|
|
|
|
match meta_item.node {
|
2018-05-14 16:11:55 +12:00
|
|
|
MetaItemKind::Word => {
|
|
|
|
let path_str = meta_item.ident.to_string();
|
|
|
|
path_str == SKIP_ANNOTATION || path_str == DEPR_SKIP_ANNOTATION
|
|
|
|
}
|
2016-12-23 11:13:00 -08:00
|
|
|
MetaItemKind::List(ref l) => {
|
2018-05-06 09:57:26 +12:00
|
|
|
meta_item.name() == "cfg_attr" && l.len() == 2 && is_skip_nested(&l[1])
|
2016-09-16 15:19:18 +12:00
|
|
|
}
|
2015-06-23 15:58:58 +02:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-16 15:19:18 +12:00
|
|
|
#[inline]
|
|
|
|
fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
|
|
|
|
match meta_item.node {
|
|
|
|
NestedMetaItemKind::MetaItem(ref mi) => is_skip(mi),
|
|
|
|
NestedMetaItemKind::Literal(_) => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-23 15:58:58 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn contains_skip(attrs: &[Attribute]) -> bool {
|
2017-06-16 08:49:49 +09:00
|
|
|
attrs
|
|
|
|
.iter()
|
|
|
|
.any(|a| a.meta().map_or(false, |a| is_skip(&a)))
|
2015-06-23 15:58:58 +02:00
|
|
|
}
|
|
|
|
|
2015-11-17 22:53:06 -06:00
|
|
|
#[inline]
|
2019-02-09 16:14:30 +09:00
|
|
|
pub fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) -> bool {
|
2015-11-17 22:53:06 -06:00
|
|
|
match expr.node {
|
2017-07-11 22:41:38 +09:00
|
|
|
ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => {
|
|
|
|
context.config.trailing_semicolon()
|
|
|
|
}
|
2015-11-17 22:53:06 -06:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-02-09 16:14:30 +09:00
|
|
|
pub fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool {
|
2015-11-17 22:53:06 -06:00
|
|
|
match stmt.node {
|
2017-07-11 21:53:10 +09:00
|
|
|
ast::StmtKind::Semi(ref expr) => match expr.node {
|
2017-11-16 16:42:07 +09:00
|
|
|
ast::ExprKind::While(..)
|
|
|
|
| ast::ExprKind::WhileLet(..)
|
|
|
|
| ast::ExprKind::Loop(..)
|
|
|
|
| ast::ExprKind::ForLoop(..) => false,
|
2017-07-11 22:41:38 +09:00
|
|
|
ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => {
|
|
|
|
context.config.trailing_semicolon()
|
|
|
|
}
|
2017-07-11 21:53:10 +09:00
|
|
|
_ => true,
|
|
|
|
},
|
2016-03-01 17:27:19 -05:00
|
|
|
ast::StmtKind::Expr(..) => false,
|
2015-11-17 22:53:06 -06:00
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-16 15:19:18 +12:00
|
|
|
#[inline]
|
|
|
|
pub fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> {
|
|
|
|
match stmt.node {
|
|
|
|
ast::StmtKind::Expr(ref expr) => Some(expr),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 15:17:40 +09:00
|
|
|
#[inline]
|
|
|
|
pub fn count_newlines(input: &str) -> usize {
|
2018-10-13 17:39:19 -03:00
|
|
|
// Using bytes to omit UTF-8 decoding
|
|
|
|
bytecount::count(input.as_bytes(), b'\n')
|
2017-12-05 15:17:40 +09:00
|
|
|
}
|
|
|
|
|
2016-07-26 06:20:01 +01:00
|
|
|
// For format_missing and last_pos, need to use the source callsite (if applicable).
|
|
|
|
// Required as generated code spans aren't guaranteed to follow on from the last span.
|
|
|
|
macro_rules! source {
|
2018-03-22 16:09:21 +09:00
|
|
|
($this:ident, $sp:expr) => {
|
2017-06-06 06:54:22 +02:00
|
|
|
$sp.source_callsite()
|
2018-02-04 12:09:03 +00:00
|
|
|
};
|
2017-06-06 06:54:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
|
2017-08-19 21:47:40 +03:00
|
|
|
Span::new(lo, hi, NO_EXPANSION)
|
2016-07-26 06:20:01 +01:00
|
|
|
}
|
2016-04-06 10:04:29 +05:30
|
|
|
|
2019-02-19 02:56:42 +00:00
|
|
|
// Returns `true` if the given span does not intersect with file lines.
|
2017-07-29 12:51:45 +09:00
|
|
|
macro_rules! out_of_file_lines_range {
|
2018-03-22 16:09:21 +09:00
|
|
|
($self:ident, $span:expr) => {
|
2018-10-15 11:52:27 +13:00
|
|
|
!$self.config.file_lines().is_all()
|
|
|
|
&& !$self
|
|
|
|
.config
|
|
|
|
.file_lines()
|
|
|
|
.intersects(&$self.source_map.lookup_line_range($span))
|
2018-02-04 12:09:03 +00:00
|
|
|
};
|
2017-07-29 12:51:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! skip_out_of_file_lines_range {
|
2018-03-22 16:09:21 +09:00
|
|
|
($self:ident, $span:expr) => {
|
2017-07-29 12:51:45 +09:00
|
|
|
if out_of_file_lines_range!($self, $span) {
|
|
|
|
return None;
|
|
|
|
}
|
2018-02-04 12:09:03 +00:00
|
|
|
};
|
2017-07-29 12:51:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! skip_out_of_file_lines_range_visitor {
|
2018-03-22 16:09:21 +09:00
|
|
|
($self:ident, $span:expr) => {
|
2017-07-29 12:51:45 +09:00
|
|
|
if out_of_file_lines_range!($self, $span) {
|
2017-07-29 17:06:50 +02:00
|
|
|
$self.push_rewrite($span, None);
|
2017-07-29 12:51:45 +09:00
|
|
|
return;
|
|
|
|
}
|
2018-02-04 12:09:03 +00:00
|
|
|
};
|
2017-07-29 12:51:45 +09:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:14:33 +09:00
|
|
|
// Wraps String in an Option. Returns Some when the string adheres to the
|
2018-07-07 12:24:09 +02:00
|
|
|
// Rewrite constraints defined for the Rewrite trait and None otherwise.
|
2017-10-07 22:04:02 +09:00
|
|
|
pub fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
|
2018-08-05 10:50:34 +09:00
|
|
|
if is_valid_str(&filter_normal_code(&s), max_width, shape) {
|
2017-10-07 22:14:33 +09:00
|
|
|
Some(s)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2015-09-04 18:09:05 +02:00
|
|
|
|
2017-10-07 22:14:33 +09:00
|
|
|
fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
|
|
|
|
if !snippet.is_empty() {
|
|
|
|
// First line must fits with `shape.width`.
|
|
|
|
if first_line_width(snippet) > shape.width {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// If the snippet does not include newline, we are done.
|
2019-01-15 08:41:09 +09:00
|
|
|
if is_single_line(snippet) {
|
2017-10-07 22:14:33 +09:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// The other lines must fit within the maximum width.
|
2019-01-15 08:41:09 +09:00
|
|
|
if snippet
|
|
|
|
.lines()
|
|
|
|
.skip(1)
|
|
|
|
.any(|line| unicode_str_width(line) > max_width)
|
|
|
|
{
|
2017-10-07 22:14:33 +09:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// A special check for the last line, since the caller may
|
|
|
|
// place trailing characters on this line.
|
|
|
|
if last_line_width(snippet) > shape.used_width() + shape.width {
|
|
|
|
return false;
|
2015-09-04 18:09:05 +02:00
|
|
|
}
|
|
|
|
}
|
2017-10-07 22:14:33 +09:00
|
|
|
true
|
2015-09-04 18:09:05 +02:00
|
|
|
}
|
|
|
|
|
2017-03-28 23:14:48 +09:00
|
|
|
#[inline]
|
2019-04-03 11:16:54 +02:00
|
|
|
pub fn colon_spaces(config: &Config) -> &'static str {
|
|
|
|
let before = config.space_before_colon();
|
|
|
|
let after = config.space_after_colon();
|
2017-03-28 23:14:48 +09:00
|
|
|
match (before, after) {
|
|
|
|
(true, true) => " : ",
|
|
|
|
(true, false) => " :",
|
|
|
|
(false, true) => ": ",
|
|
|
|
(false, false) => ":",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
chains: prefer to use the next line for an expression, if using the same line would introduce an open block or similar
This problem came to light due to the chains changes, but effects other code too. It only happens rarely, e.g.,
before this fix:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => rewrite_delimited_inner(
delim_tok,
args,
).map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)),
};
```
after:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
}
```
2018-07-11 21:01:40 +12:00
|
|
|
#[inline]
|
2016-04-14 08:36:59 +12:00
|
|
|
pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
|
|
|
|
match e.node {
|
2018-04-06 23:09:45 +09:00
|
|
|
ast::ExprKind::Call(ref e, _)
|
2017-11-16 16:42:07 +09:00
|
|
|
| ast::ExprKind::Binary(_, ref e, _)
|
|
|
|
| ast::ExprKind::Cast(ref e, _)
|
|
|
|
| ast::ExprKind::Type(ref e, _)
|
|
|
|
| ast::ExprKind::Assign(ref e, _)
|
|
|
|
| ast::ExprKind::AssignOp(_, ref e, _)
|
|
|
|
| ast::ExprKind::Field(ref e, _)
|
|
|
|
| ast::ExprKind::Index(ref e, _)
|
|
|
|
| ast::ExprKind::Range(Some(ref e), _, _)
|
|
|
|
| ast::ExprKind::Try(ref e) => left_most_sub_expr(e),
|
2016-04-14 08:36:59 +12:00
|
|
|
_ => e,
|
|
|
|
}
|
|
|
|
}
|
2017-08-15 16:49:02 +09:00
|
|
|
|
chains: prefer to use the next line for an expression, if using the same line would introduce an open block or similar
This problem came to light due to the chains changes, but effects other code too. It only happens rarely, e.g.,
before this fix:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => rewrite_delimited_inner(
delim_tok,
args,
).map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)),
};
```
after:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
}
```
2018-07-11 21:01:40 +12:00
|
|
|
#[inline]
|
2017-10-05 00:23:17 +09:00
|
|
|
pub fn starts_with_newline(s: &str) -> bool {
|
|
|
|
s.starts_with('\n') || s.starts_with("\r\n")
|
|
|
|
}
|
chains: prefer to use the next line for an expression, if using the same line would introduce an open block or similar
This problem came to light due to the chains changes, but effects other code too. It only happens rarely, e.g.,
before this fix:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => rewrite_delimited_inner(
delim_tok,
args,
).map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)),
};
```
after:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
}
```
2018-07-11 21:01:40 +12:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn first_line_ends_with(s: &str, c: char) -> bool {
|
|
|
|
s.lines().next().map_or(false, |l| l.ends_with(c))
|
|
|
|
}
|
2018-10-15 14:06:52 +13:00
|
|
|
|
|
|
|
// States whether an expression's last line exclusively consists of closing
|
|
|
|
// parens, braces, and brackets in its idiomatic formatting.
|
2019-02-09 16:14:30 +09:00
|
|
|
pub fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr: &str) -> bool {
|
2018-10-15 14:06:52 +13:00
|
|
|
match expr.node {
|
|
|
|
ast::ExprKind::Mac(..)
|
|
|
|
| ast::ExprKind::Call(..)
|
|
|
|
| ast::ExprKind::MethodCall(..)
|
|
|
|
| ast::ExprKind::Array(..)
|
|
|
|
| ast::ExprKind::Struct(..)
|
|
|
|
| ast::ExprKind::While(..)
|
|
|
|
| ast::ExprKind::WhileLet(..)
|
|
|
|
| ast::ExprKind::If(..)
|
|
|
|
| ast::ExprKind::IfLet(..)
|
|
|
|
| ast::ExprKind::Block(..)
|
|
|
|
| ast::ExprKind::Loop(..)
|
|
|
|
| ast::ExprKind::ForLoop(..)
|
|
|
|
| ast::ExprKind::Match(..) => repr.contains('\n'),
|
|
|
|
ast::ExprKind::Paren(ref expr)
|
|
|
|
| ast::ExprKind::Binary(_, _, ref expr)
|
|
|
|
| ast::ExprKind::Index(_, ref expr)
|
|
|
|
| ast::ExprKind::Unary(_, ref expr)
|
|
|
|
| ast::ExprKind::Closure(_, _, _, _, ref expr, _)
|
|
|
|
| ast::ExprKind::Try(ref expr)
|
|
|
|
| ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr),
|
|
|
|
// This can only be a string lit
|
|
|
|
ast::ExprKind::Lit(_) => {
|
|
|
|
repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces()
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2018-10-18 11:45:55 +13:00
|
|
|
|
2019-02-19 02:56:42 +00:00
|
|
|
/// Removes trailing spaces from the specified snippet. We do not remove spaces
|
2018-10-18 11:45:55 +13:00
|
|
|
/// inside strings or comments.
|
|
|
|
pub fn remove_trailing_white_spaces(text: &str) -> String {
|
|
|
|
let mut buffer = String::with_capacity(text.len());
|
|
|
|
let mut space_buffer = String::with_capacity(128);
|
|
|
|
for (char_kind, c) in CharClasses::new(text.chars()) {
|
|
|
|
match c {
|
|
|
|
'\n' => {
|
|
|
|
if char_kind == FullCodeCharKind::InString {
|
|
|
|
buffer.push_str(&space_buffer);
|
|
|
|
}
|
|
|
|
space_buffer.clear();
|
|
|
|
buffer.push('\n');
|
|
|
|
}
|
|
|
|
_ if c.is_whitespace() => {
|
|
|
|
space_buffer.push(c);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
if !space_buffer.is_empty() {
|
|
|
|
buffer.push_str(&space_buffer);
|
|
|
|
space_buffer.clear();
|
|
|
|
}
|
|
|
|
buffer.push(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer
|
|
|
|
}
|
|
|
|
|
2018-10-30 01:09:09 +01:00
|
|
|
/// Indent each line according to the specified `indent`.
|
|
|
|
/// e.g.
|
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// foo!{
|
|
|
|
/// x,
|
|
|
|
/// y,
|
|
|
|
/// foo(
|
|
|
|
/// a,
|
|
|
|
/// b,
|
|
|
|
/// c,
|
|
|
|
/// ),
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// will become
|
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// foo!{
|
|
|
|
/// x,
|
|
|
|
/// y,
|
|
|
|
/// foo(
|
|
|
|
/// a,
|
|
|
|
/// b,
|
|
|
|
/// c,
|
|
|
|
/// ),
|
|
|
|
/// }
|
|
|
|
/// ```
|
2018-11-07 01:49:53 -07:00
|
|
|
pub fn trim_left_preserve_layout(orig: &str, indent: Indent, config: &Config) -> Option<String> {
|
2018-10-30 01:09:09 +01:00
|
|
|
let mut lines = LineClasses::new(orig);
|
2018-12-18 03:21:31 +01:00
|
|
|
let first_line = lines.next().map(|(_, s)| s.trim_end().to_owned())?;
|
2018-10-30 01:09:09 +01:00
|
|
|
let mut trimmed_lines = Vec::with_capacity(16);
|
|
|
|
|
|
|
|
let mut veto_trim = false;
|
|
|
|
let min_prefix_space_width = lines
|
|
|
|
.filter_map(|(kind, line)| {
|
|
|
|
let mut trimmed = true;
|
|
|
|
let prefix_space_width = if is_empty_line(&line) {
|
|
|
|
None
|
2018-10-18 22:58:45 +02:00
|
|
|
} else {
|
2018-10-30 01:09:09 +01:00
|
|
|
Some(get_prefix_space_width(config, &line))
|
|
|
|
};
|
|
|
|
|
2019-02-07 00:05:05 +01:00
|
|
|
// 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
|
|
|
|
&& kind == FullCodeCharKind::InStringCommented))
|
2019-01-16 23:06:28 +01:00
|
|
|
&& !line.ends_with('\\');
|
2019-01-10 14:25:07 +01:00
|
|
|
let line = if veto_trim || new_veto_trim_value {
|
|
|
|
veto_trim = new_veto_trim_value;
|
2018-10-30 01:09:09 +01:00
|
|
|
trimmed = false;
|
|
|
|
line
|
|
|
|
} else {
|
|
|
|
line.trim().to_owned()
|
|
|
|
};
|
|
|
|
trimmed_lines.push((trimmed, line, prefix_space_width));
|
|
|
|
|
2019-01-10 14:25:07 +01:00
|
|
|
// Because there is a veto against trimming and indenting lines within a string,
|
|
|
|
// such lines should not be taken into account when computing the minimum.
|
2018-10-30 01:09:09 +01:00
|
|
|
match kind {
|
2019-01-16 23:06:28 +01:00
|
|
|
FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented
|
|
|
|
if config.version() == Version::Two =>
|
|
|
|
{
|
|
|
|
None
|
|
|
|
}
|
2018-10-30 01:09:09 +01:00
|
|
|
FullCodeCharKind::InString | FullCodeCharKind::EndString => None,
|
|
|
|
_ => prefix_space_width,
|
2018-10-18 22:58:45 +02:00
|
|
|
}
|
|
|
|
})
|
2018-10-30 01:09:09 +01:00
|
|
|
.min()?;
|
|
|
|
|
|
|
|
Some(
|
|
|
|
first_line
|
|
|
|
+ "\n"
|
|
|
|
+ &trimmed_lines
|
|
|
|
.iter()
|
|
|
|
.map(
|
|
|
|
|&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
|
|
|
|
_ if !trimmed => line.to_owned(),
|
|
|
|
Some(original_indent_width) => {
|
|
|
|
let new_indent_width = indent.width()
|
|
|
|
+ original_indent_width.saturating_sub(min_prefix_space_width);
|
|
|
|
let new_indent = Indent::from_width(config, new_indent_width);
|
|
|
|
format!("{}{}", new_indent.to_string(config), line)
|
|
|
|
}
|
|
|
|
None => String::new(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("\n"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-02-07 00:05:05 +01:00
|
|
|
/// Based on the given line, determine if the next line can be indented or not.
|
|
|
|
/// This allows to preserve the indentation of multi-line literals.
|
|
|
|
pub fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Config) -> bool {
|
|
|
|
!(kind.is_string() || (config.version() == Version::Two && kind.is_commented_string()))
|
|
|
|
|| line.ends_with('\\')
|
|
|
|
}
|
|
|
|
|
2018-10-30 01:09:09 +01:00
|
|
|
pub fn is_empty_line(s: &str) -> bool {
|
|
|
|
s.is_empty() || s.chars().all(char::is_whitespace)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_prefix_space_width(config: &Config, s: &str) -> usize {
|
|
|
|
let mut width = 0;
|
|
|
|
for c in s.chars() {
|
|
|
|
match c {
|
|
|
|
' ' => width += 1,
|
|
|
|
'\t' => width += config.tab_spaces(),
|
|
|
|
_ => return width,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
width
|
2018-10-18 22:58:45 +02:00
|
|
|
}
|
|
|
|
|
2018-12-08 14:39:52 +09:00
|
|
|
pub(crate) trait NodeIdExt {
|
|
|
|
fn root() -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NodeIdExt for NodeId {
|
|
|
|
fn root() -> NodeId {
|
|
|
|
NodeId::placeholder_from_mark(Mark::root())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 08:41:09 +09:00
|
|
|
pub(crate) fn unicode_str_width(s: &str) -> usize {
|
|
|
|
s.width()
|
|
|
|
}
|
|
|
|
|
2019-03-18 21:41:31 +09:00
|
|
|
pub fn get_skip_macro_names(attrs: &[ast::Attribute]) -> Vec<String> {
|
|
|
|
let mut skip_macro_names = vec![];
|
|
|
|
for attr in attrs {
|
|
|
|
// syntax::ast::Path is implemented partialEq
|
|
|
|
// but it is designed for segments.len() == 1
|
|
|
|
if format!("{}", attr.path) != "rustfmt::skip::macros" {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(list) = attr.meta_item_list() {
|
|
|
|
for spanned in list {
|
|
|
|
if let Some(name) = spanned.name() {
|
|
|
|
skip_macro_names.push(name.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
skip_macro_names
|
|
|
|
}
|
|
|
|
|
2018-10-30 01:09:09 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_remove_trailing_white_spaces() {
|
|
|
|
let s = " r#\"\n test\n \"#";
|
|
|
|
assert_eq!(remove_trailing_white_spaces(&s), s);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_trim_left_preserve_layout() {
|
|
|
|
let s = "aaa\n\tbbb\n ccc";
|
|
|
|
let config = Config::default();
|
|
|
|
let indent = Indent::new(4, 0);
|
|
|
|
assert_eq!(
|
2018-11-07 01:49:53 -07:00
|
|
|
trim_left_preserve_layout(&s, indent, &config),
|
2018-10-30 01:09:09 +01:00
|
|
|
Some("aaa\n bbb\n ccc".to_string())
|
|
|
|
);
|
|
|
|
}
|
2018-10-18 11:45:55 +13:00
|
|
|
}
|