Implement --fix with stdin (#405)

This commit is contained in:
fsouza 2022-10-12 22:31:46 -04:00 committed by GitHub
parent 2f69be0d41
commit 720bfe0161
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 33 deletions

View File

@ -1,7 +1,3 @@
use std::fs;
use std::path::Path;
use anyhow::Result;
use itertools::Itertools; use itertools::Itertools;
use rustpython_parser::ast::Location; use rustpython_parser::ast::Location;
@ -24,17 +20,15 @@ impl From<bool> for Mode {
} }
/// Auto-fix errors in a file, and write the fixed source code to disk. /// Auto-fix errors in a file, and write the fixed source code to disk.
pub fn fix_file(checks: &mut [Check], contents: &str, path: &Path) -> Result<()> { pub fn fix_file(checks: &mut [Check], contents: &str) -> Option<String> {
if checks.iter().all(|check| check.fix.is_none()) { if checks.iter().all(|check| check.fix.is_none()) {
return Ok(()); return None;
} }
let output = apply_fixes( Some(apply_fixes(
checks.iter_mut().filter_map(|check| check.fix.as_mut()), checks.iter_mut().filter_map(|check| check.fix.as_mut()),
contents, contents,
); ))
fs::write(path, output).map_err(|e| e.into())
} }
/// Apply a series of fixes. /// Apply a series of fixes.

View File

@ -1,4 +1,6 @@
use std::fs::write; use std::fs::write;
use std::io;
use std::io::Write;
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
@ -83,7 +85,12 @@ pub(crate) fn check_path(
Ok(checks) Ok(checks)
} }
pub fn lint_stdin(path: &Path, stdin: &str, settings: &Settings) -> Result<Vec<Message>> { pub fn lint_stdin(
path: &Path,
stdin: &str,
settings: &Settings,
autofix: &fixer::Mode,
) -> Result<Vec<Message>> {
// Tokenize once. // Tokenize once.
let tokens: Vec<LexResult> = tokenize(stdin); let tokens: Vec<LexResult> = tokenize(stdin);
@ -91,14 +98,16 @@ pub fn lint_stdin(path: &Path, stdin: &str, settings: &Settings) -> Result<Vec<M
let noqa_line_for = noqa::extract_noqa_line_for(&tokens); let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
// Generate checks. // Generate checks.
let checks = check_path( let mut checks = check_path(path, stdin, tokens, &noqa_line_for, settings, autofix)?;
path,
stdin, // Apply autofix, write results to stdout.
tokens, if matches!(autofix, fixer::Mode::Apply) {
&noqa_line_for, let output = match fix_file(&mut checks, stdin) {
settings, None => stdin.to_string(),
&fixer::Mode::None, Some(content) => content,
)?; };
io::stdout().write_all(output.as_bytes())?;
}
// Convert to messages. // Convert to messages.
Ok(checks Ok(checks
@ -141,7 +150,9 @@ pub fn lint_path(
// Apply autofix. // Apply autofix.
if matches!(autofix, fixer::Mode::Apply) { if matches!(autofix, fixer::Mode::Apply) {
fix_file(&mut checks, &contents, path)?; if let Some(fixed_contents) = fix_file(&mut checks, &contents) {
write(path, fixed_contents)?;
}
}; };
// Convert to messages. // Convert to messages.

View File

@ -81,9 +81,9 @@ fn read_from_stdin() -> Result<String> {
Ok(buffer) Ok(buffer)
} }
fn run_once_stdin(settings: &Settings, filename: &Path) -> Result<Vec<Message>> { fn run_once_stdin(settings: &Settings, filename: &Path, autofix: bool) -> Result<Vec<Message>> {
let stdin = read_from_stdin()?; let stdin = read_from_stdin()?;
let mut messages = lint_stdin(filename, &stdin, settings)?; let mut messages = lint_stdin(filename, &stdin, settings, &autofix.into())?;
messages.sort_unstable(); messages.sort_unstable();
Ok(messages) Ok(messages)
} }
@ -365,18 +365,20 @@ fn inner_main() -> Result<ExitCode> {
println!("Formatted {modifications} files."); println!("Formatted {modifications} files.");
} }
} else { } else {
let messages = if cli.files == vec![PathBuf::from("-")] { let (messages, print_messages) = if cli.files == vec![PathBuf::from("-")] {
if cli.fix {
eprintln!("Warning: --fix is not enabled when reading from stdin.");
}
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string()); let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
let path = Path::new(&filename); let path = Path::new(&filename);
run_once_stdin(&settings, path)? (
run_once_stdin(&settings, path, cli.fix)?,
!cli.quiet && !cli.fix,
)
} else { } else {
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)? (
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?,
!cli.quiet,
)
}; };
if !cli.quiet { if print_messages {
printer.write_once(&messages)?; printer.write_once(&messages)?;
} }

View File

@ -39,9 +39,42 @@ fn test_stdin_autofix() -> Result<()> {
let mut cmd = Command::cargo_bin(crate_name!())?; let mut cmd = Command::cargo_bin(crate_name!())?;
let output = cmd let output = cmd
.args(&["-", "--fix"]) .args(&["-", "--fix"])
.write_stdin("import os\n") .write_stdin("import os\nimport sys\n\nprint(sys.version)\n")
.assert() .assert()
.failure(); .success();
assert!(str::from_utf8(&output.get_output().stdout)?.contains("-:1:1: F401")); assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"import sys\n\nprint(sys.version)\n"
);
Ok(())
}
#[test]
fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<()> {
let mut cmd = Command::cargo_bin(crate_name!())?;
let output = cmd
.args(&["-", "--fix"])
.write_stdin("import os\nimport sys\n\nif (1, 2):\n print(sys.version)\n")
.assert()
.failure();
assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"import sys\n\nif (1, 2):\n print(sys.version)\n"
);
Ok(())
}
#[test]
fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()> {
let mut cmd = Command::cargo_bin(crate_name!())?;
let output = cmd
.args(&["-", "--fix"])
.write_stdin("import sys\n\nprint(sys.version)\n")
.assert()
.success();
assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"import sys\n\nprint(sys.version)\n"
);
Ok(()) Ok(())
} }