Skip to content

Commit 89940e5

Browse files
authored
Fix bugs related to file-lines (rust-lang#3684)
1 parent 6487422 commit 89940e5

File tree

14 files changed

+211
-99
lines changed

14 files changed

+211
-99
lines changed

src/bin/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ fn make_opts() -> Options {
167167
}
168168

169169
fn is_nightly() -> bool {
170-
option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "nightly" || c == "dev")
170+
option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev")
171171
}
172172

173173
// Returned i32 is an exit code

src/config/file_lines.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{cmp, fmt, iter, str};
99
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
1010
use serde_json as json;
1111

12-
use syntax::source_map::{self, SourceFile};
12+
use syntax::source_map::{self, SourceFile, SourceMap, Span};
1313

1414
/// A range of lines in a file, inclusive of both ends.
1515
pub struct LineRange {
@@ -78,6 +78,17 @@ impl LineRange {
7878
pub fn file_name(&self) -> FileName {
7979
self.file.name.clone().into()
8080
}
81+
82+
pub(crate) fn from_span(source_map: &SourceMap, span: Span) -> LineRange {
83+
let lo_char_pos = source_map.lookup_char_pos(span.lo());
84+
let hi_char_pos = source_map.lookup_char_pos(span.hi());
85+
debug_assert!(lo_char_pos.file.name == hi_char_pos.file.name);
86+
LineRange {
87+
file: lo_char_pos.file.clone(),
88+
lo: lo_char_pos.line,
89+
hi: hi_char_pos.line,
90+
}
91+
}
8192
}
8293

8394
/// A range that is inclusive of both ends.

src/missed_spans.rs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::config::file_lines::FileLines;
77
use crate::config::{EmitMode, FileName};
88
use crate::shape::{Indent, Shape};
99
use crate::source_map::LineRangeUtils;
10-
use crate::utils::{count_newlines, last_line_width, mk_sp};
10+
use crate::utils::{count_lf_crlf, count_newlines, last_line_width, mk_sp};
1111
use crate::visitor::FmtVisitor;
1212

1313
struct SnippetStatus {
@@ -157,7 +157,7 @@ impl<'a> FmtVisitor<'a> {
157157
fn write_snippet_inner<F>(
158158
&mut self,
159159
big_snippet: &str,
160-
mut big_diff: usize,
160+
big_diff: usize,
161161
old_snippet: &str,
162162
span: Span,
163163
process_last_snippet: F,
@@ -176,36 +176,26 @@ impl<'a> FmtVisitor<'a> {
176176
_ => Cow::from(old_snippet),
177177
};
178178

179-
// if the snippet starts with a new line, then information about the lines needs to be
180-
// adjusted since it is off by 1.
181-
let snippet = if snippet.starts_with('\n') {
182-
// this takes into account the blank_lines_* options
183-
self.push_vertical_spaces(1);
184-
// include the newline character into the big_diff
185-
big_diff += 1;
186-
status.cur_line += 1;
187-
&snippet[1..]
188-
} else {
189-
snippet
190-
};
191-
192-
let slice_within_file_lines_range = |file_lines: FileLines, cur_line, s| -> (usize, bool) {
193-
let newline_count = count_newlines(s);
194-
let within_file_lines_range = file_lines.contains_range(
195-
file_name,
196-
cur_line,
197-
// if a newline character is at the end of the slice, then the number of newlines
198-
// needs to be decreased by 1 so that the range checked against the file_lines is
199-
// the visual range one would expect.
200-
cur_line + newline_count - if s.ends_with('\n') { 1 } else { 0 },
201-
);
202-
(newline_count, within_file_lines_range)
203-
};
179+
let slice_within_file_lines_range =
180+
|file_lines: FileLines, cur_line, s| -> (usize, usize, bool) {
181+
let (lf_count, crlf_count) = count_lf_crlf(s);
182+
let newline_count = lf_count + crlf_count;
183+
let within_file_lines_range = file_lines.contains_range(
184+
file_name,
185+
cur_line,
186+
// if a newline character is at the end of the slice, then the number of
187+
// newlines needs to be decreased by 1 so that the range checked against
188+
// the file_lines is the visual range one would expect.
189+
cur_line + newline_count - if s.ends_with('\n') { 1 } else { 0 },
190+
);
191+
(lf_count, crlf_count, within_file_lines_range)
192+
};
204193
for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
205194
debug!("{:?}: {:?}", kind, subslice);
206195

207-
let (newline_count, within_file_lines_range) =
196+
let (lf_count, crlf_count, within_file_lines_range) =
208197
slice_within_file_lines_range(self.config.file_lines(), status.cur_line, subslice);
198+
let newline_count = lf_count + crlf_count;
209199
if CodeCharKind::Comment == kind && within_file_lines_range {
210200
// 1: comment.
211201
self.process_comment(
@@ -219,15 +209,15 @@ impl<'a> FmtVisitor<'a> {
219209
// 2: blank lines.
220210
self.push_vertical_spaces(newline_count);
221211
status.cur_line += newline_count;
222-
status.line_start = offset + newline_count;
212+
status.line_start = offset + lf_count + crlf_count * 2;
223213
} else {
224214
// 3: code which we failed to format or which is not within file-lines range.
225215
self.process_missing_code(&mut status, snippet, subslice, offset, file_name);
226216
}
227217
}
228218

229219
let last_snippet = &snippet[status.line_start..];
230-
let (_, within_file_lines_range) =
220+
let (_, _, within_file_lines_range) =
231221
slice_within_file_lines_range(self.config.file_lines(), status.cur_line, last_snippet);
232222
if within_file_lines_range {
233223
process_last_snippet(self, last_snippet, snippet);

src/source_map.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use syntax::source_map::{BytePos, SourceMap, Span};
55

66
use crate::comment::FindUncommented;
77
use crate::config::file_lines::LineRange;
8+
use crate::utils::starts_with_newline;
89
use crate::visitor::SnippetProvider;
910

1011
pub(crate) trait SpanUtils {
@@ -95,7 +96,7 @@ impl LineRangeUtils for SourceMap {
9596

9697
// in case the span starts with a newline, the line range is off by 1 without the
9798
// adjustment below
98-
let offset = 1 + if snippet.starts_with('\n') { 1 } else { 0 };
99+
let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
99100
// Line numbers start at 1
100101
LineRange {
101102
file: lo.sf.clone(),

src/utils.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,22 @@ pub(crate) fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> {
306306
}
307307
}
308308

309-
#[inline]
309+
/// Returns the number of LF and CRLF respectively.
310+
pub(crate) fn count_lf_crlf(input: &str) -> (usize, usize) {
311+
let mut lf = 0;
312+
let mut crlf = 0;
313+
let mut is_crlf = false;
314+
for c in input.as_bytes() {
315+
match c {
316+
b'\r' => is_crlf = true,
317+
b'\n' if is_crlf => crlf += 1,
318+
b'\n' => lf += 1,
319+
_ => is_crlf = false,
320+
}
321+
}
322+
(lf, crlf)
323+
}
324+
310325
pub(crate) fn count_newlines(input: &str) -> usize {
311326
// Using bytes to omit UTF-8 decoding
312327
bytecount::count(input.as_bytes(), b'\n')

src/visitor.rs

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use syntax::{ast, visit};
66

77
use crate::attr::*;
88
use crate::comment::{CodeCharKind, CommentCodeSlices};
9-
use crate::config::file_lines::FileName;
9+
use crate::config::file_lines::LineRange;
1010
use crate::config::{BraceStyle, Config};
1111
use crate::items::{
1212
format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item,
@@ -89,6 +89,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
8989
Shape::indented(self.block_indent, self.config)
9090
}
9191

92+
fn next_span(&self, hi: BytePos) -> Span {
93+
mk_sp(self.last_pos, hi)
94+
}
95+
9296
fn visit_stmt(&mut self, stmt: &Stmt<'_>) {
9397
debug!(
9498
"visit_stmt: {:?} {:?}",
@@ -132,31 +136,19 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
132136
}
133137
}
134138

135-
pub(crate) fn visit_block(
139+
/// Remove spaces between the opening brace and the first statement or the inner attribute
140+
/// of the block.
141+
fn trim_spaces_after_opening_brace(
136142
&mut self,
137143
b: &ast::Block,
138144
inner_attrs: Option<&[ast::Attribute]>,
139-
has_braces: bool,
140145
) {
141-
debug!(
142-
"visit_block: {:?} {:?}",
143-
self.source_map.lookup_char_pos(b.span.lo()),
144-
self.source_map.lookup_char_pos(b.span.hi())
145-
);
146-
147-
// Check if this block has braces.
148-
let brace_compensation = BytePos(if has_braces { 1 } else { 0 });
149-
150-
self.last_pos = self.last_pos + brace_compensation;
151-
self.block_indent = self.block_indent.block_indent(self.config);
152-
self.push_str("{");
153-
154146
if let Some(first_stmt) = b.stmts.first() {
155147
let hi = inner_attrs
156148
.and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo()))
157149
.unwrap_or_else(|| first_stmt.span().lo());
158-
159-
let snippet = self.snippet(mk_sp(self.last_pos, hi));
150+
let missing_span = self.next_span(hi);
151+
let snippet = self.snippet(missing_span);
160152
let len = CommentCodeSlices::new(snippet)
161153
.nth(0)
162154
.and_then(|(kind, _, s)| {
@@ -170,19 +162,57 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
170162
self.last_pos = self.last_pos + BytePos::from_usize(len);
171163
}
172164
}
165+
}
173166

174-
// Format inner attributes if available.
175-
let skip_rewrite = if let Some(attrs) = inner_attrs {
176-
self.visit_attrs(attrs, ast::AttrStyle::Inner)
177-
} else {
178-
false
179-
};
167+
/// Returns the total length of the spaces which should be trimmed between the last statement
168+
/// and the closing brace of the block.
169+
fn trimmed_spaces_width_before_closing_brace(
170+
&mut self,
171+
b: &ast::Block,
172+
brace_compensation: BytePos,
173+
) -> usize {
174+
match b.stmts.last() {
175+
None => 0,
176+
Some(..) => {
177+
let span_after_last_stmt = self.next_span(b.span.hi() - brace_compensation);
178+
let missing_snippet = self.snippet(span_after_last_stmt);
179+
CommentCodeSlices::new(missing_snippet)
180+
.last()
181+
.and_then(|(kind, _, s)| {
182+
if kind == CodeCharKind::Normal && s.trim().is_empty() {
183+
Some(s.len())
184+
} else {
185+
None
186+
}
187+
})
188+
.unwrap_or(0)
189+
}
190+
}
191+
}
180192

181-
if skip_rewrite {
182-
self.push_rewrite(b.span, None);
183-
self.close_block(false, b.span);
184-
self.last_pos = source!(self, b.span).hi();
185-
return;
193+
pub(crate) fn visit_block(
194+
&mut self,
195+
b: &ast::Block,
196+
inner_attrs: Option<&[ast::Attribute]>,
197+
has_braces: bool,
198+
) {
199+
debug!(
200+
"visit_block: {:?} {:?}",
201+
self.source_map.lookup_char_pos(b.span.lo()),
202+
self.source_map.lookup_char_pos(b.span.hi())
203+
);
204+
205+
// Check if this block has braces.
206+
let brace_compensation = BytePos(if has_braces { 1 } else { 0 });
207+
208+
self.last_pos = self.last_pos + brace_compensation;
209+
self.block_indent = self.block_indent.block_indent(self.config);
210+
self.push_str("{");
211+
self.trim_spaces_after_opening_brace(b, inner_attrs);
212+
213+
// Format inner attributes if available.
214+
if let Some(attrs) = inner_attrs {
215+
self.visit_attrs(attrs, ast::AttrStyle::Inner);
186216
}
187217

188218
self.walk_block_stmts(b);
@@ -195,36 +225,22 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
195225
}
196226
}
197227

198-
let mut remove_len = BytePos(0);
199-
if let Some(stmt) = b.stmts.last() {
200-
let span_after_last_stmt = mk_sp(
201-
stmt.span.hi(),
202-
source!(self, b.span).hi() - brace_compensation,
203-
);
204-
// if the span is outside of a file_lines range, then do not try to remove anything
205-
if !out_of_file_lines_range!(self, span_after_last_stmt) {
206-
let snippet = self.snippet(span_after_last_stmt);
207-
let len = CommentCodeSlices::new(snippet)
208-
.last()
209-
.and_then(|(kind, _, s)| {
210-
if kind == CodeCharKind::Normal && s.trim().is_empty() {
211-
Some(s.len())
212-
} else {
213-
None
214-
}
215-
});
216-
if let Some(len) = len {
217-
remove_len = BytePos::from_usize(len);
218-
}
219-
}
228+
let missing_span = self.next_span(b.span.hi());
229+
if out_of_file_lines_range!(self, missing_span) {
230+
self.push_str(self.snippet(missing_span));
231+
self.block_indent = self.block_indent.block_unindent(self.config);
232+
self.last_pos = source!(self, b.span).hi();
233+
return;
220234
}
221235

236+
let remove_len = BytePos::from_usize(
237+
self.trimmed_spaces_width_before_closing_brace(b, brace_compensation),
238+
);
222239
let unindent_comment = self.is_if_else_block && !b.stmts.is_empty() && {
223240
let end_pos = source!(self, b.span).hi() - brace_compensation - remove_len;
224241
let snippet = self.snippet(mk_sp(self.last_pos, end_pos));
225242
snippet.contains("//") || snippet.contains("/*")
226243
};
227-
// FIXME: we should compress any newlines here to just one
228244
if unindent_comment {
229245
self.block_indent = self.block_indent.block_unindent(self.config);
230246
}
@@ -234,7 +250,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
234250
if unindent_comment {
235251
self.block_indent = self.block_indent.block_indent(self.config);
236252
}
237-
self.close_block(unindent_comment, b.span);
253+
self.close_block(unindent_comment, self.next_span(b.span.hi()));
238254
self.last_pos = source!(self, b.span).hi();
239255
}
240256

@@ -243,12 +259,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
243259
// The closing brace itself, however, should be indented at a shallower
244260
// level.
245261
fn close_block(&mut self, unindent_comment: bool, span: Span) {
246-
let file_name: FileName = self.source_map.span_to_filename(span).into();
247262
let skip_this_line = !self
248263
.config
249264
.file_lines()
250-
.contains_line(&file_name, self.line_number);
251-
if !skip_this_line {
265+
.contains(&LineRange::from_span(self.source_map, span));
266+
if skip_this_line {
267+
self.push_str(self.snippet(span));
268+
} else {
252269
let total_len = self.buffer.len();
253270
let chars_too_many = if unindent_comment {
254271
0
@@ -271,8 +288,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
271288
self.buffer.push_str("\n");
272289
}
273290
}
291+
self.push_str("}");
274292
}
275-
self.push_str("}");
276293
self.block_indent = self.block_indent.block_unindent(self.config);
277294
}
278295

@@ -800,8 +817,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
800817
self.block_indent = self.block_indent.block_indent(self.config);
801818
self.visit_attrs(attrs, ast::AttrStyle::Inner);
802819
self.walk_mod_items(m);
803-
self.format_missing_with_indent(source!(self, m.inner).hi() - BytePos(1));
804-
self.close_block(false, m.inner);
820+
let missing_span = mk_sp(source!(self, m.inner).hi() - BytePos(1), m.inner.hi());
821+
self.format_missing_with_indent(missing_span.lo());
822+
self.close_block(false, missing_span);
805823
}
806824
self.last_pos = source!(self, m.inner).hi();
807825
} else {
@@ -823,9 +841,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
823841
pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) {
824842
while let Some(pos) = self
825843
.snippet_provider
826-
.opt_span_after(mk_sp(self.last_pos, end_pos), "\n")
844+
.opt_span_after(self.next_span(end_pos), "\n")
827845
{
828-
if let Some(snippet) = self.opt_snippet(mk_sp(self.last_pos, pos)) {
846+
if let Some(snippet) = self.opt_snippet(self.next_span(pos)) {
829847
if snippet.trim().is_empty() {
830848
self.last_pos = pos;
831849
} else {

0 commit comments

Comments
 (0)