Skip to content

Commit da118cd

Browse files
committed
Backport 4236
Read the original file for comparing/detecting newline (4236) Closes 4097 The code base diverged enough where a simple cherry-pick wasn't possible, but implementing the logic to read the input file when `NewlineStyle::Auto` was simple enough to implement. New tests were added to hopefully prevent regressions.
1 parent 2403f82 commit da118cd

File tree

5 files changed

+183
-8
lines changed

5 files changed

+183
-8
lines changed

Cargo.lock

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ rustc-workspace-hack = "1.0.0"
6666

6767
# Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them.
6868

69+
[dev-dependencies]
70+
tempdir = "0.3.7"
71+
6972
[package.metadata.rust-analyzer]
7073
# This package uses #[feature(rustc_private)]
7174
rustc_private = true

src/config/file_lines.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use itertools::Itertools;
44
use std::collections::HashMap;
5-
use std::path::PathBuf;
5+
use std::path::{Path, PathBuf};
66
use std::{cmp, fmt, iter, str};
77

88
use rustc_data_structures::sync::Lrc;
@@ -25,6 +25,15 @@ pub enum FileName {
2525
Stdin,
2626
}
2727

28+
impl FileName {
29+
pub(crate) fn as_path(&self) -> Option<&Path> {
30+
match self {
31+
FileName::Real(ref path) => Some(path),
32+
_ => None,
33+
}
34+
}
35+
}
36+
2837
impl From<rustc_span::FileName> for FileName {
2938
fn from(name: rustc_span::FileName) -> FileName {
3039
match name {

src/formatting.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
230230

231231
apply_newline_style(
232232
self.config.newline_style(),
233+
&path,
233234
&mut visitor.buffer,
234235
snippet_provider.entire_snippet(),
235236
);

src/formatting/newline_style.rs

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::NewlineStyle;
1+
use crate::{FileName, NewlineStyle};
2+
use std::borrow::Cow;
3+
use std::fs;
24

35
/// Apply this newline style to the formatted text. When the style is set
46
/// to `Auto`, the `raw_input_text` is used to detect the existing line
@@ -8,10 +10,11 @@ use crate::NewlineStyle;
810
/// newlines, the `Native` style will be used.
911
pub(crate) fn apply_newline_style(
1012
newline_style: NewlineStyle,
13+
path: &FileName,
1114
formatted_text: &mut String,
1215
raw_input_text: &str,
1316
) {
14-
*formatted_text = match effective_newline_style(newline_style, raw_input_text) {
17+
*formatted_text = match effective_newline_style(newline_style, path, raw_input_text) {
1518
EffectiveNewlineStyle::Windows => convert_to_windows_newlines(formatted_text),
1619
EffectiveNewlineStyle::Unix => convert_to_unix_newlines(formatted_text),
1720
}
@@ -25,10 +28,19 @@ enum EffectiveNewlineStyle {
2528

2629
fn effective_newline_style(
2730
newline_style: NewlineStyle,
31+
path: &FileName,
2832
raw_input_text: &str,
2933
) -> EffectiveNewlineStyle {
3034
match newline_style {
31-
NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
35+
NewlineStyle::Auto => match path.as_path() {
36+
Some(path) => auto_detect_newline_style(
37+
&fs::read_to_string(path)
38+
.map(|s| Cow::Owned(s))
39+
.unwrap_or_else(|_| Cow::Borrowed(raw_input_text)),
40+
),
41+
None => auto_detect_newline_style(raw_input_text),
42+
},
43+
3244
NewlineStyle::Native => native_newline_style(),
3345
NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
3446
NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
@@ -84,6 +96,8 @@ fn convert_to_unix_newlines(formatted_text: &str) -> String {
8496
#[cfg(test)]
8597
mod tests {
8698
use super::*;
99+
use std::fs;
100+
use tempdir::TempDir;
87101

88102
#[test]
89103
fn auto_detects_unix_newlines() {
@@ -128,7 +142,28 @@ mod tests {
128142
let raw_input_text = "One\nTwo\nThree";
129143

130144
let mut out = String::from(formatted_text);
131-
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
145+
apply_newline_style(
146+
NewlineStyle::Auto,
147+
&FileName::Stdin,
148+
&mut out,
149+
raw_input_text,
150+
);
151+
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
152+
}
153+
154+
#[test]
155+
fn auto_detects_and_applies_unix_newlines_from_real_file() {
156+
let formatted_text = "One\nTwo\nThree";
157+
let raw_input_text = "One\nTwo\nThree";
158+
159+
let tmpdir = TempDir::new("unix_newlines_from_real_file").unwrap();
160+
let path = tmpdir.path().join("test.rs");
161+
fs::write(&path, raw_input_text).unwrap();
162+
163+
let path = FileName::Real(path);
164+
165+
let mut out = String::from(formatted_text);
166+
apply_newline_style(NewlineStyle::Auto, &path, &mut out, raw_input_text);
132167
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
133168
}
134169

@@ -138,17 +173,81 @@ mod tests {
138173
let raw_input_text = "One\r\nTwo\r\nThree";
139174

140175
let mut out = String::from(formatted_text);
141-
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
176+
apply_newline_style(
177+
NewlineStyle::Auto,
178+
&FileName::Stdin,
179+
&mut out,
180+
raw_input_text,
181+
);
142182
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
143183
}
144184

185+
#[test]
186+
fn auto_detects_and_applies_windows_newlines_from_real_file() {
187+
let formatted_text = "One\nTwo\nThree";
188+
let raw_input_text = "One\r\nTwo\r\nThree";
189+
190+
let tmpdir = TempDir::new("windows_newlines_from_real_file").unwrap();
191+
let path = tmpdir.path().join("test.rs");
192+
fs::write(&path, raw_input_text).unwrap();
193+
194+
let path = FileName::Real(path);
195+
196+
let mut out = String::from(formatted_text);
197+
apply_newline_style(NewlineStyle::Auto, &path, &mut out, raw_input_text);
198+
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
199+
}
200+
201+
#[test]
202+
fn auto_detect_and_applies_windows_newlines_if_windows_newlines_read_from_file() {
203+
// Even if the `raw_input_text` does not contain crlf chars, we should still apply
204+
// Them if we can read crlf from the input `FileName::Real(path)`.
205+
// see issue https://github.com/rust-lang/rustfmt/issues/4097
206+
let text = "One\nTwo\nThree";
207+
208+
let tmpdir = TempDir::new("windows_newlines_if_windows_newlines_read_from_file").unwrap();
209+
let path = tmpdir.path().join("test.rs");
210+
fs::write(&path, "One\r\nTwo\r\nThree").unwrap();
211+
212+
let path = FileName::Real(path);
213+
214+
let mut out = String::from(text);
215+
// Note that the source file contains `crlf`, while the `raw_input_text` contains `lf`
216+
apply_newline_style(NewlineStyle::Auto, &path, &mut out, text);
217+
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
218+
}
219+
220+
#[test]
221+
fn auto_detect_and_applies_unix_newlines_if_unix_newlines_read_from_file() {
222+
// This following test is unlikely to happen in practice, but it is meant to illustrate
223+
// that line endings found in the source files take precedence over the `raw_input_text`
224+
// passed to apply_newline_style.
225+
let text = "One\r\nTwo\r\nThree";
226+
227+
let tmpdir = TempDir::new("unix_newlines_if_unix_newlines_read_from_file").unwrap();
228+
let path = tmpdir.path().join("test.rs");
229+
fs::write(&path, "One\nTwo\nThree").unwrap();
230+
231+
let path = FileName::Real(path);
232+
233+
let mut out = String::from(text);
234+
// Note that the source file contains `lf`, while the `raw_input_text` contains `crlf`
235+
apply_newline_style(NewlineStyle::Auto, &path, &mut out, text);
236+
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'crlf'");
237+
}
238+
145239
#[test]
146240
fn auto_detects_and_applies_native_newlines() {
147241
let formatted_text = "One\nTwo\nThree";
148242
let raw_input_text = "One Two Three";
149243

150244
let mut out = String::from(formatted_text);
151-
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
245+
apply_newline_style(
246+
NewlineStyle::Auto,
247+
&FileName::Stdin,
248+
&mut out,
249+
raw_input_text,
250+
);
152251

153252
if cfg!(windows) {
154253
assert_eq!(
@@ -244,7 +343,7 @@ mod tests {
244343
newline_style: NewlineStyle,
245344
) {
246345
let mut out = String::from(input);
247-
apply_newline_style(newline_style, &mut out, input);
346+
apply_newline_style(newline_style, &FileName::Stdin, &mut out, input);
248347
assert_eq!(expected, &out);
249348
}
250349
}

0 commit comments

Comments
 (0)