Skip to content

Commit 0156fe0

Browse files
authored
Merge pull request #2589 from topecongiro/issue-2588
Do not indent or unindent inside string literal
2 parents 4237f40 + ec1907b commit 0156fe0

File tree

5 files changed

+135
-36
lines changed

5 files changed

+135
-36
lines changed

src/comment.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,45 @@ where
901901
}
902902
}
903903

904+
/// An iterator over the lines of a string, paired with the char kind at the
905+
/// end of the line.
906+
pub struct LineClasses<'a> {
907+
base: iter::Peekable<CharClasses<std::str::Chars<'a>>>,
908+
kind: FullCodeCharKind,
909+
}
910+
911+
impl<'a> LineClasses<'a> {
912+
pub fn new(s: &'a str) -> Self {
913+
LineClasses {
914+
base: CharClasses::new(s.chars()).peekable(),
915+
kind: FullCodeCharKind::Normal,
916+
}
917+
}
918+
}
919+
920+
impl<'a> Iterator for LineClasses<'a> {
921+
type Item = (FullCodeCharKind, String);
922+
923+
fn next(&mut self) -> Option<Self::Item> {
924+
if self.base.peek().is_none() {
925+
return None;
926+
}
927+
928+
let mut line = String::new();
929+
930+
while let Some((kind, c)) = self.base.next() {
931+
self.kind = kind;
932+
if c == '\n' {
933+
break;
934+
} else {
935+
line.push(c);
936+
}
937+
}
938+
939+
Some((self.kind, line))
940+
}
941+
}
942+
904943
/// Iterator over functional and commented parts of a string. Any part of a string is either
905944
/// functional code, either *one* block comment, either *one* line comment. Whitespace between
906945
/// comments is functional code. Line comments contain their ending newlines.
@@ -1141,8 +1180,7 @@ fn remove_comment_header(comment: &str) -> &str {
11411180

11421181
#[cfg(test)]
11431182
mod test {
1144-
use super::{contains_comment, rewrite_comment, CharClasses, CodeCharKind, CommentCodeSlices,
1145-
FindUncommented, FullCodeCharKind};
1183+
use super::*;
11461184
use shape::{Indent, Shape};
11471185

11481186
#[test]
@@ -1298,4 +1336,10 @@ mod test {
12981336
check("\"/* abc */\"", "abc", Some(4));
12991337
check("\"/* abc", "abc", Some(4));
13001338
}
1339+
1340+
#[test]
1341+
fn test_remove_trailing_white_spaces() {
1342+
let s = format!(" r#\"\n test\n \"#");
1343+
assert_eq!(remove_trailing_white_spaces(&s), s);
1344+
}
13011345
}

src/lib.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use syntax::errors::{DiagnosticBuilder, Handler};
4747
use syntax::parse::{self, ParseSess};
4848

4949
use checkstyle::{output_footer, output_header};
50-
use comment::{CharClasses, FullCodeCharKind};
50+
use comment::{CharClasses, FullCodeCharKind, LineClasses};
5151
use issues::{BadIssueSeeker, Issue};
5252
use shape::Indent;
5353
use utils::use_colored_tty;
@@ -605,10 +605,19 @@ const FN_MAIN_PREFIX: &str = "fn main() {\n";
605605

606606
fn enclose_in_main_block(s: &str, config: &Config) -> String {
607607
let indent = Indent::from_width(config, config.tab_spaces());
608-
FN_MAIN_PREFIX.to_owned() + &indent.to_string(config)
609-
+ &s.lines()
610-
.collect::<Vec<_>>()
611-
.join(&indent.to_string_with_newline(config)) + "\n}"
608+
let mut result = String::with_capacity(s.len() * 2);
609+
result.push_str(FN_MAIN_PREFIX);
610+
let mut need_indent = true;
611+
for (kind, line) in LineClasses::new(s) {
612+
if need_indent {
613+
result.push_str(&indent.to_string(config));
614+
}
615+
result.push_str(&line);
616+
result.push('\n');
617+
need_indent = !(kind.is_string() && !line.ends_with('\\'));
618+
}
619+
result.push('}');
620+
result
612621
}
613622

614623
/// Format the given code block. Mainly targeted for code block in comment.
@@ -626,13 +635,16 @@ pub fn format_code_block(code_snippet: &str, config: &Config) -> Option<String>
626635
let formatted = format_snippet(&snippet, config)?;
627636
// 2 = "}\n"
628637
let block_len = formatted.len().checked_sub(2).unwrap_or(0);
629-
for line in formatted[FN_MAIN_PREFIX.len()..block_len].lines() {
638+
let mut is_indented = true;
639+
for (kind, ref line) in LineClasses::new(&formatted[FN_MAIN_PREFIX.len()..block_len]) {
630640
if !is_first {
631641
result.push('\n');
632642
} else {
633643
is_first = false;
634644
}
635-
let trimmed_line = if line.len() > config.max_width() {
645+
let trimmed_line = if !is_indented {
646+
line
647+
} else if line.len() > config.max_width() {
636648
// If there are lines that are larger than max width, we cannot tell
637649
// whether we have succeeded but have some comments or strings that
638650
// are too long, or we have failed to format code block. We will be
@@ -655,6 +667,7 @@ pub fn format_code_block(code_snippet: &str, config: &Config) -> Option<String>
655667
line
656668
};
657669
result.push_str(trimmed_line);
670+
is_indented = !(kind.is_string() && !line.ends_with('\\'));
658671
}
659672
Some(result)
660673
}

src/macros.rs

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use syntax::{ast, ptr};
3434

3535
use codemap::SpanUtils;
3636
use comment::{contains_comment, remove_trailing_white_spaces, CharClasses, FindUncommented,
37-
FullCodeCharKind};
37+
FullCodeCharKind, LineClasses};
3838
use expr::rewrite_array;
3939
use lists::{itemize_list, write_list, ListFormatting};
4040
use overflow;
@@ -1054,18 +1054,27 @@ fn indent_macro_snippet(
10541054
macro_str: &str,
10551055
indent: Indent,
10561056
) -> Option<String> {
1057-
let mut lines = macro_str.lines();
1058-
let first_line = lines.next().map(|s| s.trim_right())?;
1057+
let mut lines = LineClasses::new(macro_str);
1058+
let first_line = lines.next().map(|(_, s)| s.trim_right().to_owned())?;
10591059
let mut trimmed_lines = Vec::with_capacity(16);
10601060

1061+
let mut veto_trim = false;
10611062
let min_prefix_space_width = lines
1062-
.filter_map(|line| {
1063-
let prefix_space_width = if is_empty_line(line) {
1063+
.filter_map(|(kind, line)| {
1064+
let mut trimmed = true;
1065+
let prefix_space_width = if is_empty_line(&line) {
10641066
None
10651067
} else {
1066-
Some(get_prefix_space_width(context, line))
1068+
Some(get_prefix_space_width(context, &line))
10671069
};
1068-
trimmed_lines.push((line.trim(), prefix_space_width));
1070+
let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) {
1071+
veto_trim = kind.is_string() && !line.ends_with('\\');
1072+
trimmed = false;
1073+
line
1074+
} else {
1075+
line.trim().to_owned()
1076+
};
1077+
trimmed_lines.push((trimmed, line, prefix_space_width));
10691078
prefix_space_width
10701079
})
10711080
.min()?;
@@ -1074,17 +1083,20 @@ fn indent_macro_snippet(
10741083
String::from(first_line) + "\n"
10751084
+ &trimmed_lines
10761085
.iter()
1077-
.map(|&(line, prefix_space_width)| match prefix_space_width {
1078-
Some(original_indent_width) => {
1079-
let new_indent_width = indent.width()
1080-
+ original_indent_width
1081-
.checked_sub(min_prefix_space_width)
1082-
.unwrap_or(0);
1083-
let new_indent = Indent::from_width(context.config, new_indent_width);
1084-
format!("{}{}", new_indent.to_string(context.config), line.trim())
1085-
}
1086-
None => String::new(),
1087-
})
1086+
.map(
1087+
|&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
1088+
_ if !trimmed => line.to_owned(),
1089+
Some(original_indent_width) => {
1090+
let new_indent_width = indent.width()
1091+
+ original_indent_width
1092+
.checked_sub(min_prefix_space_width)
1093+
.unwrap_or(0);
1094+
let new_indent = Indent::from_width(context.config, new_indent_width);
1095+
format!("{}{}", new_indent.to_string(context.config), line.trim())
1096+
}
1097+
None => String::new(),
1098+
},
1099+
)
10881100
.collect::<Vec<_>>()
10891101
.join("\n"),
10901102
)
@@ -1231,15 +1243,17 @@ impl MacroBranch {
12311243

12321244
// Indent the body since it is in a block.
12331245
let indent_str = body_indent.to_string(&config);
1234-
let mut new_body = new_body
1235-
.trim_right()
1236-
.lines()
1237-
.fold(String::new(), |mut s, l| {
1238-
if !l.is_empty() {
1239-
s += &indent_str;
1240-
}
1241-
s + l + "\n"
1242-
});
1246+
let mut new_body = LineClasses::new(new_body.trim_right())
1247+
.fold(
1248+
(String::new(), true),
1249+
|(mut s, need_indent), (kind, ref l)| {
1250+
if !l.is_empty() && need_indent {
1251+
s += &indent_str;
1252+
}
1253+
(s + l + "\n", !(kind.is_string() && !l.ends_with('\\')))
1254+
},
1255+
)
1256+
.0;
12431257

12441258
// Undo our replacement of macro variables.
12451259
// FIXME: this could be *much* more efficient.

tests/source/macros.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,20 @@ macro lex_err($kind: ident $(, $body: expr)*) {
348348
methods![ get, post, delete, ];
349349
methods!( get, post, delete, );
350350

351+
// #2588
352+
macro_rules! m {
353+
() => {
354+
r#"
355+
test
356+
"#
357+
};
358+
}
359+
fn foo() {
360+
f!{r#"
361+
test
362+
"#};
363+
}
364+
351365
// #2591
352366
fn foo() {
353367
match 0u32 {

tests/target/macros.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,20 @@ macro lex_err($kind: ident $(, $body: expr)*) {
923923
methods![get, post, delete,];
924924
methods!(get, post, delete,);
925925

926+
// #2588
927+
macro_rules! m {
928+
() => {
929+
r#"
930+
test
931+
"#
932+
};
933+
}
934+
fn foo() {
935+
f!{r#"
936+
test
937+
"#};
938+
}
939+
926940
// #2591
927941
fn foo() {
928942
match 0u32 {

0 commit comments

Comments
 (0)