Skip to content

Commit cdd08da

Browse files
committed
fix line numbering in missed spans and handle file_lines in edge cases
- a leading/trailing newline character in missed spans was throwing off the start/end of ranges used to compare against file_lines - fix handling of file_lines when closing a block Close rust-lang#3442
1 parent 1427e4c commit cdd08da

File tree

4 files changed

+91
-36
lines changed

4 files changed

+91
-36
lines changed

src/missed_spans.rs

+37-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::borrow::Cow;
33
use syntax::source_map::{BytePos, Pos, Span};
44

55
use crate::comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
6+
use crate::config::file_lines::FileLines;
67
use crate::config::{EmitMode, FileName};
78
use crate::shape::{Indent, Shape};
89
use crate::source_map::LineRangeUtils;
@@ -156,7 +157,7 @@ impl<'a> FmtVisitor<'a> {
156157
fn write_snippet_inner<F>(
157158
&mut self,
158159
big_snippet: &str,
159-
big_diff: usize,
160+
mut big_diff: usize,
160161
old_snippet: &str,
161162
span: Span,
162163
process_last_snippet: F,
@@ -175,16 +176,36 @@ impl<'a> FmtVisitor<'a> {
175176
_ => Cow::from(old_snippet),
176177
};
177178

178-
for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
179-
debug!("{:?}: {:?}", kind, subslice);
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+
};
180191

181-
let newline_count = count_newlines(subslice);
182-
let within_file_lines_range = self.config.file_lines().contains_range(
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(
183195
file_name,
184-
status.cur_line,
185-
status.cur_line + newline_count,
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 },
186201
);
202+
(newline_count, within_file_lines_range)
203+
};
204+
for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
205+
debug!("{:?}: {:?}", kind, subslice);
187206

207+
let (newline_count, within_file_lines_range) =
208+
slice_within_file_lines_range(self.config.file_lines(), status.cur_line, subslice);
188209
if CodeCharKind::Comment == kind && within_file_lines_range {
189210
// 1: comment.
190211
self.process_comment(
@@ -205,7 +226,15 @@ impl<'a> FmtVisitor<'a> {
205226
}
206227
}
207228

208-
process_last_snippet(self, &snippet[status.line_start..], snippet);
229+
let last_snippet = &snippet[status.line_start..];
230+
let (_, within_file_lines_range) =
231+
slice_within_file_lines_range(self.config.file_lines(), status.cur_line, last_snippet);
232+
if within_file_lines_range {
233+
process_last_snippet(self, last_snippet, snippet);
234+
} else {
235+
// just append what's left
236+
self.push_str(last_snippet);
237+
}
209238
}
210239

211240
fn process_comment(

src/source_map.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl<'a> SpanUtils for SnippetProvider<'a> {
7171

7272
impl LineRangeUtils for SourceMap {
7373
fn lookup_line_range(&self, span: Span) -> LineRange {
74+
let snippet = self.span_to_snippet(span).unwrap_or(String::new());
7475
let lo = self.lookup_line(span.lo()).unwrap();
7576
let hi = self.lookup_line(span.hi()).unwrap();
7677

@@ -80,11 +81,14 @@ impl LineRangeUtils for SourceMap {
8081
lo, hi
8182
);
8283

84+
// in case the span starts with a newline, the line range is off by 1 without the
85+
// adjustment below
86+
let offset = 1 + if snippet.starts_with('\n') { 1 } else { 0 };
8387
// Line numbers start at 1
8488
LineRange {
8589
file: lo.sf.clone(),
86-
lo: lo.line + 1,
87-
hi: hi.line + 1,
90+
lo: lo.line + offset,
91+
hi: hi.line + offset,
8892
}
8993
}
9094
}

src/visitor.rs

+38-26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use syntax::{ast, visit};
66

77
use crate::attr::*;
88
use crate::comment::{CodeCharKind, CommentCodeSlices, FindUncommented};
9+
use crate::config::file_lines::FileName;
910
use crate::config::{BraceStyle, Config, Version};
1011
use crate::expr::{format_expr, ExprType};
1112
use crate::items::{
@@ -170,7 +171,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
170171

171172
if skip_rewrite {
172173
self.push_rewrite(b.span, None);
173-
self.close_block(false);
174+
self.close_block(false, b.span);
174175
self.last_pos = source!(self, b.span).hi();
175176
return;
176177
}
@@ -187,21 +188,25 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
187188

188189
let mut remove_len = BytePos(0);
189190
if let Some(stmt) = b.stmts.last() {
190-
let snippet = self.snippet(mk_sp(
191+
let span_after_last_stmt = mk_sp(
191192
stmt.span.hi(),
192193
source!(self, b.span).hi() - brace_compensation,
193-
));
194-
let len = CommentCodeSlices::new(snippet)
195-
.last()
196-
.and_then(|(kind, _, s)| {
197-
if kind == CodeCharKind::Normal && s.trim().is_empty() {
198-
Some(s.len())
199-
} else {
200-
None
201-
}
202-
});
203-
if let Some(len) = len {
204-
remove_len = BytePos::from_usize(len);
194+
);
195+
// if the span is outside of a file_lines range, then do not try to remove anything
196+
if !out_of_file_lines_range!(self, span_after_last_stmt) {
197+
let snippet = self.snippet(span_after_last_stmt);
198+
let len = CommentCodeSlices::new(snippet)
199+
.last()
200+
.and_then(|(kind, _, s)| {
201+
if kind == CodeCharKind::Normal && s.trim().is_empty() {
202+
Some(s.len())
203+
} else {
204+
None
205+
}
206+
});
207+
if let Some(len) = len {
208+
remove_len = BytePos::from_usize(len);
209+
}
205210
}
206211
}
207212

@@ -220,24 +225,31 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
220225
if unindent_comment {
221226
self.block_indent = self.block_indent.block_indent(self.config);
222227
}
223-
self.close_block(unindent_comment);
228+
self.close_block(unindent_comment, b.span);
224229
self.last_pos = source!(self, b.span).hi();
225230
}
226231

227232
// FIXME: this is a terrible hack to indent the comments between the last
228233
// item in the block and the closing brace to the block's level.
229234
// The closing brace itself, however, should be indented at a shallower
230235
// level.
231-
fn close_block(&mut self, unindent_comment: bool) {
232-
let total_len = self.buffer.len();
233-
let chars_too_many = if unindent_comment {
234-
0
235-
} else if self.config.hard_tabs() {
236-
1
237-
} else {
238-
self.config.tab_spaces()
239-
};
240-
self.buffer.truncate(total_len - chars_too_many);
236+
fn close_block(&mut self, unindent_comment: bool, span: Span) {
237+
let file_name: FileName = self.source_map.span_to_filename(span).into();
238+
let skip_this_line = !self
239+
.config
240+
.file_lines()
241+
.contains_line(&file_name, self.line_number);
242+
if !skip_this_line {
243+
let total_len = self.buffer.len();
244+
let chars_too_many = if unindent_comment {
245+
0
246+
} else if self.config.hard_tabs() {
247+
1
248+
} else {
249+
self.config.tab_spaces()
250+
};
251+
self.buffer.truncate(total_len - chars_too_many);
252+
}
241253
self.push_str("}");
242254
self.block_indent = self.block_indent.block_unindent(self.config);
243255
}
@@ -759,7 +771,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
759771
self.visit_attrs(attrs, ast::AttrStyle::Inner);
760772
self.walk_mod_items(m);
761773
self.format_missing_with_indent(source!(self, m.inner).hi() - BytePos(1));
762-
self.close_block(false);
774+
self.close_block(false, m.inner);
763775
}
764776
self.last_pos = source!(self, m.inner).hi();
765777
} else {

tests/target/issue-3442.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// rustfmt-file_lines: [{"file":"tests/target/issue-3442.rs","range":[5,5]},{"file":"tests/target/issue-3442.rs","range":[8,8]}]
2+
3+
extern crate alpha; // comment 1
4+
extern crate beta; // comment 2
5+
#[allow(aaa)] // comment 3
6+
#[macro_use]
7+
extern crate gamma;
8+
#[allow(bbb)] // comment 4
9+
#[macro_use]
10+
extern crate lazy_static;

0 commit comments

Comments
 (0)