Skip to content

Backport 4236 #5439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ rustc-workspace-hack = "1.0.0"

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

[dev-dependencies]
tempdir = "0.3.7"

[package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)]
rustc_private = true
11 changes: 10 additions & 1 deletion src/config/file_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use itertools::Itertools;
use std::collections::HashMap;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::{cmp, fmt, iter, str};

use rustc_data_structures::sync::Lrc;
Expand All @@ -25,6 +25,15 @@ pub enum FileName {
Stdin,
}

impl FileName {
pub(crate) fn as_path(&self) -> Option<&Path> {
match self {
FileName::Real(ref path) => Some(path),
_ => None,
}
}
}

impl From<rustc_span::FileName> for FileName {
fn from(name: rustc_span::FileName) -> FileName {
match name {
Expand Down
1 change: 1 addition & 0 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {

apply_newline_style(
self.config.newline_style(),
&path,
&mut visitor.buffer,
snippet_provider.entire_snippet(),
);
Expand Down
120 changes: 110 additions & 10 deletions src/formatting/newline_style.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
use crate::NewlineStyle;
use crate::{FileName, NewlineStyle};
use std::borrow::Cow;
use std::fs;

/// Apply this newline style to the formatted text. When the style is set
/// to `Auto`, the `raw_input_text` is used to detect the existing line
/// endings.
/// to `Auto`, the `path` and `raw_input_text` are used to detect the existing line
/// endings. The `path` is prefered when present, and the `raw_input_text` is used
/// when the input was passed from stdin, or we fail to read the file content from the `path`.
///
/// If the style is set to `Auto` and `raw_input_text` contains no
/// If the style is set to `Auto` and `path` or `raw_input_text` contain no
/// newlines, the `Native` style will be used.
pub(crate) fn apply_newline_style(
newline_style: NewlineStyle,
path: &FileName,
formatted_text: &mut String,
raw_input_text: &str,
) {
*formatted_text = match effective_newline_style(newline_style, raw_input_text) {
*formatted_text = match effective_newline_style(newline_style, path, raw_input_text) {
EffectiveNewlineStyle::Windows => convert_to_windows_newlines(formatted_text),
EffectiveNewlineStyle::Unix => convert_to_unix_newlines(formatted_text),
}
Expand All @@ -25,10 +29,19 @@ enum EffectiveNewlineStyle {

fn effective_newline_style(
newline_style: NewlineStyle,
path: &FileName,
raw_input_text: &str,
) -> EffectiveNewlineStyle {
match newline_style {
NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
NewlineStyle::Auto => match path.as_path() {
Some(path) => auto_detect_newline_style(
&fs::read_to_string(path)
.map(|s| Cow::Owned(s))
.unwrap_or_else(|_| Cow::Borrowed(raw_input_text)),
),
None => auto_detect_newline_style(raw_input_text),
},

NewlineStyle::Native => native_newline_style(),
NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
Expand Down Expand Up @@ -84,6 +97,8 @@ fn convert_to_unix_newlines(formatted_text: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempdir::TempDir;

#[test]
fn auto_detects_unix_newlines() {
Expand Down Expand Up @@ -128,7 +143,28 @@ mod tests {
let raw_input_text = "One\nTwo\nThree";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
apply_newline_style(
NewlineStyle::Auto,
&FileName::Stdin,
&mut out,
raw_input_text,
);
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
}

#[test]
fn auto_detects_and_applies_unix_newlines_from_real_file() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\nTwo\nThree";

let tmpdir = TempDir::new("unix_newlines_from_real_file").unwrap();
let path = tmpdir.path().join("test.rs");
fs::write(&path, raw_input_text).unwrap();

let path = FileName::Real(path);

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &path, &mut out, raw_input_text);
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
}

Expand All @@ -138,17 +174,81 @@ mod tests {
let raw_input_text = "One\r\nTwo\r\nThree";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
apply_newline_style(
NewlineStyle::Auto,
&FileName::Stdin,
&mut out,
raw_input_text,
);
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
}

#[test]
fn auto_detects_and_applies_windows_newlines_from_real_file() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\r\nTwo\r\nThree";

let tmpdir = TempDir::new("windows_newlines_from_real_file").unwrap();
let path = tmpdir.path().join("test.rs");
fs::write(&path, raw_input_text).unwrap();

let path = FileName::Real(path);

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &path, &mut out, raw_input_text);
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
}

#[test]
fn auto_detect_and_applies_windows_newlines_if_windows_newlines_read_from_file() {
// Even if the `raw_input_text` does not contain crlf chars, we should still apply
// Them if we can read crlf from the input `FileName::Real(path)`.
// see issue https://github.com/rust-lang/rustfmt/issues/4097
let text = "One\nTwo\nThree";

let tmpdir = TempDir::new("windows_newlines_if_windows_newlines_read_from_file").unwrap();
let path = tmpdir.path().join("test.rs");
fs::write(&path, "One\r\nTwo\r\nThree").unwrap();

let path = FileName::Real(path);

let mut out = String::from(text);
// Note that the source file contains `crlf`, while the `raw_input_text` contains `lf`
apply_newline_style(NewlineStyle::Auto, &path, &mut out, text);
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
}

#[test]
fn auto_detect_and_applies_unix_newlines_if_unix_newlines_read_from_file() {
// This following test is unlikely to happen in practice, but it is meant to illustrate
// that line endings found in the source files take precedence over the `raw_input_text`
// passed to apply_newline_style.
let text = "One\r\nTwo\r\nThree";

let tmpdir = TempDir::new("unix_newlines_if_unix_newlines_read_from_file").unwrap();
let path = tmpdir.path().join("test.rs");
fs::write(&path, "One\nTwo\nThree").unwrap();

let path = FileName::Real(path);

let mut out = String::from(text);
// Note that the source file contains `lf`, while the `raw_input_text` contains `crlf`
apply_newline_style(NewlineStyle::Auto, &path, &mut out, text);
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'crlf'");
}

#[test]
fn auto_detects_and_applies_native_newlines() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One Two Three";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
apply_newline_style(
NewlineStyle::Auto,
&FileName::Stdin,
&mut out,
raw_input_text,
);

if cfg!(windows) {
assert_eq!(
Expand Down Expand Up @@ -244,7 +344,7 @@ mod tests {
newline_style: NewlineStyle,
) {
let mut out = String::from(input);
apply_newline_style(newline_style, &mut out, input);
apply_newline_style(newline_style, &FileName::Stdin, &mut out, input);
assert_eq!(expected, &out);
}
}