Generate source code with detected line ending (#1487)

This commit is contained in:
Reiner Gerecke 2022-12-31 14:02:29 +01:00 committed by GitHub
parent ba9cf70917
commit c0fc55b812
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 147 additions and 31 deletions

View File

@ -22,7 +22,11 @@ pub fn main(cli: &Cli) -> Result<()> {
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?; let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
let locator = SourceCodeLocator::new(&contents); let locator = SourceCodeLocator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator);
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_suite(&python_ast); generator.unparse_suite(&python_ast);
println!("{}", generator.generate()?); println!("{}", generator.generate()?);
Ok(()) Ok(())

View File

@ -47,8 +47,11 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test)); let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
let mut generator = let mut generator = SourceCodeGenerator::new(
SourceCodeGenerator::new(checker.style.indentation(), checker.style.quote()); checker.style.indentation(),
checker.style.quote(),
checker.style.line_ending(),
);
generator.unparse_stmt(&assertion_error(msg)); generator.unparse_stmt(&assertion_error(msg));
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {
check.amend(Fix::replacement( check.amend(Fix::replacement(

View File

@ -54,8 +54,11 @@ fn duplicate_handler_exceptions<'a>(
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
let mut generator = let mut generator = SourceCodeGenerator::new(
SourceCodeGenerator::new(checker.style.indentation(), checker.style.quote()); checker.style.indentation(),
checker.style.quote(),
checker.style.line_ending(),
);
if unique_elts.len() == 1 { if unique_elts.len() == 1 {
generator.unparse_expr(unique_elts[0], 0); generator.unparse_expr(unique_elts[0], 0);
} else { } else {

View File

@ -46,8 +46,11 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
let mut check = Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr)); let mut check = Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr));
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
let mut generator = let mut generator = SourceCodeGenerator::new(
SourceCodeGenerator::new(checker.style.indentation(), checker.style.quote()); checker.style.indentation(),
checker.style.quote(),
checker.style.line_ending(),
);
generator.unparse_expr(&attribute(obj, value), 0); generator.unparse_expr(&attribute(obj, value), 0);
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {
check.amend(Fix::replacement( check.amend(Fix::replacement(

View File

@ -23,8 +23,11 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
Range::from_located(type_), Range::from_located(type_),
); );
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
let mut generator = let mut generator = SourceCodeGenerator::new(
SourceCodeGenerator::new(checker.style.indentation(), checker.style.quote()); checker.style.indentation(),
checker.style.quote(),
checker.style.line_ending(),
);
generator.unparse_expr(elt, 0); generator.unparse_expr(elt, 0);
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {
check.amend(Fix::replacement( check.amend(Fix::replacement(

View File

@ -34,7 +34,11 @@ fn assignment(
type_comment: None, type_comment: None,
}, },
); );
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_stmt(&stmt); generator.unparse_stmt(&stmt);
generator.generate().map_err(std::convert::Into::into) generator.generate().map_err(std::convert::Into::into)
} }

View File

@ -30,7 +30,11 @@ fn compare(
comparators: comparators.to_vec(), comparators: comparators.to_vec(),
}, },
); );
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_expr(&cmp, 0); generator.unparse_expr(&cmp, 0);
generator.generate().ok() generator.generate().ok()
} }
@ -302,7 +306,11 @@ fn function(
type_comment: None, type_comment: None,
}, },
); );
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_stmt(&func); generator.unparse_stmt(&func);
Ok(generator.generate()?) Ok(generator.generate()?)
} }

View File

@ -166,7 +166,11 @@ fn convert_to_class(
base_class: &ExprKind, base_class: &ExprKind,
stylist: &SourceCodeStyleDetector, stylist: &SourceCodeStyleDetector,
) -> Result<Fix> { ) -> Result<Fix> {
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_stmt(&create_class_def_stmt(typename, body, base_class)); generator.unparse_stmt(&create_class_def_stmt(typename, body, base_class));
let content = generator.generate()?; let content = generator.generate()?;
Ok(Fix::replacement( Ok(Fix::replacement(

View File

@ -199,7 +199,11 @@ fn convert_to_class(
base_class: &ExprKind, base_class: &ExprKind,
stylist: &SourceCodeStyleDetector, stylist: &SourceCodeStyleDetector,
) -> Result<Fix> { ) -> Result<Fix> {
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_stmt(&create_class_def_stmt( generator.unparse_stmt(&create_class_def_stmt(
class_name, class_name,
body, body,

View File

@ -138,7 +138,11 @@ fn replace_by_expr_kind(
) -> Result<Check> { ) -> Result<Check> {
let mut check = Check::new(CheckKind::RemoveSixCompat, Range::from_located(expr)); let mut check = Check::new(CheckKind::RemoveSixCompat, Range::from_located(expr));
if patch { if patch {
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_expr(&create_expr(node), 0); generator.unparse_expr(&create_expr(node), 0);
let content = generator.generate()?; let content = generator.generate()?;
check.amend(Fix::replacement( check.amend(Fix::replacement(
@ -158,7 +162,11 @@ fn replace_by_stmt_kind(
) -> Result<Check> { ) -> Result<Check> {
let mut check = Check::new(CheckKind::RemoveSixCompat, Range::from_located(expr)); let mut check = Check::new(CheckKind::RemoveSixCompat, Range::from_located(expr));
if patch { if patch {
let mut generator = SourceCodeGenerator::new(stylist.indentation(), stylist.quote()); let mut generator = SourceCodeGenerator::new(
stylist.indentation(),
stylist.quote(),
stylist.line_ending(),
);
generator.unparse_stmt(&create_stmt(node)); generator.unparse_stmt(&create_stmt(node));
let content = generator.generate()?; let content = generator.generate()?;
check.amend(Fix::replacement( check.amend(Fix::replacement(

View File

@ -65,8 +65,11 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
if checker.match_typing_call_path(&call_path, "Optional") { if checker.match_typing_call_path(&call_path, "Optional") {
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr)); let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
let mut generator = let mut generator = SourceCodeGenerator::new(
SourceCodeGenerator::new(checker.style.indentation(), checker.style.quote()); checker.style.indentation(),
checker.style.quote(),
checker.style.line_ending(),
);
generator.unparse_expr(&optional(slice), 0); generator.unparse_expr(&optional(slice), 0);
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {
check.amend(Fix::replacement( check.amend(Fix::replacement(
@ -88,6 +91,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
let mut generator = SourceCodeGenerator::new( let mut generator = SourceCodeGenerator::new(
checker.style.indentation(), checker.style.indentation(),
checker.style.quote(), checker.style.quote(),
checker.style.line_ending(),
); );
generator.unparse_expr(&union(elts), 0); generator.unparse_expr(&union(elts), 0);
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {
@ -103,6 +107,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
let mut generator = SourceCodeGenerator::new( let mut generator = SourceCodeGenerator::new(
checker.style.indentation(), checker.style.indentation(),
checker.style.quote(), checker.style.quote(),
checker.style.line_ending(),
); );
generator.unparse_expr(slice, 0); generator.unparse_expr(slice, 0);
if let Ok(content) = generator.generate() { if let Ok(content) = generator.generate() {

View File

@ -11,7 +11,7 @@ use rustpython_parser::ast::{
Operator, Stmt, StmtKind, Operator, Stmt, StmtKind,
}; };
use crate::source_code_style::{Indentation, Quote}; use crate::source_code_style::{Indentation, LineEnding, Quote};
use crate::vendor::{bytes, str}; use crate::vendor::{bytes, str};
mod precedence { mod precedence {
@ -37,6 +37,8 @@ pub struct SourceCodeGenerator<'a> {
indent: &'a Indentation, indent: &'a Indentation,
/// The quote style to use for string literals. /// The quote style to use for string literals.
quote: &'a Quote, quote: &'a Quote,
/// The line ending to use.
line_ending: &'a LineEnding,
buffer: Vec<u8>, buffer: Vec<u8>,
indent_depth: usize, indent_depth: usize,
num_newlines: usize, num_newlines: usize,
@ -44,11 +46,12 @@ pub struct SourceCodeGenerator<'a> {
} }
impl<'a> SourceCodeGenerator<'a> { impl<'a> SourceCodeGenerator<'a> {
pub fn new(indent: &'a Indentation, quote: &'a Quote) -> Self { pub fn new(indent: &'a Indentation, quote: &'a Quote, line_ending: &'a LineEnding) -> Self {
SourceCodeGenerator { SourceCodeGenerator {
// Style preferences. // Style preferences.
indent, indent,
quote, quote,
line_ending,
// Internal state. // Internal state.
buffer: vec![], buffer: vec![],
indent_depth: 0, indent_depth: 0,
@ -84,7 +87,7 @@ impl<'a> SourceCodeGenerator<'a> {
fn p(&mut self, s: &str) { fn p(&mut self, s: &str) {
if self.num_newlines > 0 { if self.num_newlines > 0 {
for _ in 0..self.num_newlines { for _ in 0..self.num_newlines {
self.buffer.extend("\n".as_bytes()); self.buffer.extend(self.line_ending.as_bytes());
} }
self.num_newlines = 0; self.num_newlines = 0;
} }
@ -944,7 +947,7 @@ impl<'a> SourceCodeGenerator<'a> {
} }
fn unparse_formatted<U>(&mut self, val: &Expr<U>, conversion: usize, spec: Option<&Expr<U>>) { fn unparse_formatted<U>(&mut self, val: &Expr<U>, conversion: usize, spec: Option<&Expr<U>>) {
let mut generator = SourceCodeGenerator::new(self.indent, self.quote); let mut generator = SourceCodeGenerator::new(self.indent, self.quote, self.line_ending);
generator.unparse_expr(val, precedence::TEST + 1); generator.unparse_expr(val, precedence::TEST + 1);
let brace = if generator.buffer.starts_with("{".as_bytes()) { let brace = if generator.buffer.starts_with("{".as_bytes()) {
// put a space to avoid escaping the bracket // put a space to avoid escaping the bracket
@ -1000,7 +1003,7 @@ impl<'a> SourceCodeGenerator<'a> {
self.unparse_fstring_body(values, is_spec); self.unparse_fstring_body(values, is_spec);
} else { } else {
self.p("f"); self.p("f");
let mut generator = SourceCodeGenerator::new(self.indent, self.quote); let mut generator = SourceCodeGenerator::new(self.indent, self.quote, self.line_ending);
generator.unparse_fstring_body(values, is_spec); generator.unparse_fstring_body(values, is_spec);
let body = std::str::from_utf8(&generator.buffer).unwrap(); let body = std::str::from_utf8(&generator.buffer).unwrap();
self.p(&format!("{}", str::repr(body, self.quote.into()))); self.p(&format!("{}", str::repr(body, self.quote.into())));
@ -1031,22 +1034,28 @@ mod tests {
use rustpython_parser::parser; use rustpython_parser::parser;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code_generator::SourceCodeGenerator;
use crate::source_code_style::{Indentation, Quote}; use crate::source_code_style::{Indentation, LineEnding, Quote};
fn round_trip(contents: &str) -> Result<String> { fn round_trip(contents: &str) -> Result<String> {
let indentation = Indentation::default(); let indentation = Indentation::default();
let quote = Quote::default(); let quote = Quote::default();
let line_ending = LineEnding::default();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let mut generator = SourceCodeGenerator::new(&indentation, &quote); let mut generator = SourceCodeGenerator::new(&indentation, &quote, &line_ending);
generator.unparse_stmt(stmt); generator.unparse_stmt(stmt);
generator.generate().map_err(std::convert::Into::into) generator.generate().map_err(std::convert::Into::into)
} }
fn round_trip_with(indentation: &Indentation, quote: &Quote, contents: &str) -> Result<String> { fn round_trip_with(
indentation: &Indentation,
quote: &Quote,
line_ending: &LineEnding,
contents: &str,
) -> Result<String> {
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let mut generator = SourceCodeGenerator::new(indentation, quote); let mut generator = SourceCodeGenerator::new(indentation, quote, line_ending);
generator.unparse_stmt(stmt); generator.unparse_stmt(stmt);
generator.generate().map_err(std::convert::Into::into) generator.generate().map_err(std::convert::Into::into)
} }
@ -1087,19 +1096,39 @@ if True:
#[test] #[test]
fn set_quote() -> Result<()> { fn set_quote() -> Result<()> {
assert_eq!( assert_eq!(
round_trip_with(&Indentation::default(), &Quote::Double, r#""hello""#)?, round_trip_with(
&Indentation::default(),
&Quote::Double,
&LineEnding::default(),
r#""hello""#
)?,
r#""hello""# r#""hello""#
); );
assert_eq!( assert_eq!(
round_trip_with(&Indentation::default(), &Quote::Single, r#""hello""#)?, round_trip_with(
&Indentation::default(),
&Quote::Single,
&LineEnding::default(),
r#""hello""#
)?,
r#"'hello'"# r#"'hello'"#
); );
assert_eq!( assert_eq!(
round_trip_with(&Indentation::default(), &Quote::Double, r#"'hello'"#)?, round_trip_with(
&Indentation::default(),
&Quote::Double,
&LineEnding::default(),
r#"'hello'"#
)?,
r#""hello""# r#""hello""#
); );
assert_eq!( assert_eq!(
round_trip_with(&Indentation::default(), &Quote::Single, r#"'hello'"#)?, round_trip_with(
&Indentation::default(),
&Quote::Single,
&LineEnding::default(),
r#"'hello'"#
)?,
r#"'hello'"# r#"'hello'"#
); );
Ok(()) Ok(())
@ -1111,6 +1140,7 @@ if True:
round_trip_with( round_trip_with(
&Indentation::new(" ".to_string()), &Indentation::new(" ".to_string()),
&Quote::default(), &Quote::default(),
&LineEnding::default(),
r#" r#"
if True: if True:
pass pass
@ -1127,6 +1157,7 @@ if True:
round_trip_with( round_trip_with(
&Indentation::new(" ".to_string()), &Indentation::new(" ".to_string()),
&Quote::default(), &Quote::default(),
&LineEnding::default(),
r#" r#"
if True: if True:
pass pass
@ -1143,6 +1174,7 @@ if True:
round_trip_with( round_trip_with(
&Indentation::new("\t".to_string()), &Indentation::new("\t".to_string()),
&Quote::default(), &Quote::default(),
&LineEnding::default(),
r#" r#"
if True: if True:
pass pass
@ -1158,4 +1190,39 @@ if True:
Ok(()) Ok(())
} }
#[test]
fn set_line_ending() -> Result<()> {
assert_eq!(
round_trip_with(
&Indentation::default(),
&Quote::default(),
&LineEnding::Lf,
"if True:\n print(42)",
)?,
"if True:\n print(42)",
);
assert_eq!(
round_trip_with(
&Indentation::default(),
&Quote::default(),
&LineEnding::CrLf,
"if True:\n print(42)",
)?,
"if True:\r\n print(42)",
);
assert_eq!(
round_trip_with(
&Indentation::default(),
&Quote::default(),
&LineEnding::Cr,
"if True:\n print(42)",
)?,
"if True:\r print(42)",
);
Ok(())
}
} }