Skip to content

Commit

Permalink
Include fixes in JSON API output (#988)
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh authored Dec 1, 2022
1 parent af40e64 commit 46f5053
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 19 deletions.
46 changes: 32 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use log::{debug, error};
use notify::{raw_watcher, RecursiveMode, Watcher};
#[cfg(not(target_family = "wasm"))]
use rayon::prelude::*;
use ruff::autofix::fixer;
use rustpython_ast::Location;
use walkdir::DirEntry;

Expand All @@ -60,14 +61,23 @@ fn read_from_stdin() -> Result<String> {
Ok(buffer)
}

fn run_once_stdin(settings: &Settings, filename: &Path, autofix: bool) -> Result<Diagnostics> {
fn run_once_stdin(
settings: &Settings,
filename: &Path,
autofix: &fixer::Mode,
) -> Result<Diagnostics> {
let stdin = read_from_stdin()?;
let mut diagnostics = lint_stdin(filename, &stdin, settings, &autofix.into())?;
let mut diagnostics = lint_stdin(filename, &stdin, settings, autofix)?;
diagnostics.messages.sort_unstable();
Ok(diagnostics)
}

fn run_once(files: &[PathBuf], settings: &Settings, cache: bool, autofix: bool) -> Diagnostics {
fn run_once(
files: &[PathBuf],
settings: &Settings,
cache: bool,
autofix: &fixer::Mode,
) -> Diagnostics {
// Collect all the files to check.
let start = Instant::now();
let paths: Vec<Result<DirEntry, walkdir::Error>> = files
Expand All @@ -83,7 +93,7 @@ fn run_once(files: &[PathBuf], settings: &Settings, cache: bool, autofix: bool)
match entry {
Ok(entry) => {
let path = entry.path();
lint_path(path, settings, &cache.into(), &autofix.into())
lint_path(path, settings, &cache.into(), autofix)
.map_err(|e| (Some(path.to_owned()), e.to_string()))
}
Err(e) => Err((
Expand All @@ -99,6 +109,7 @@ fn run_once(files: &[PathBuf], settings: &Settings, cache: bool, autofix: bool)
kind: CheckKind::IOError(message),
location: Location::default(),
end_location: Location::default(),
fix: None,
filename: path.to_string_lossy().to_string(),
source: None,
}])
Expand Down Expand Up @@ -266,7 +277,13 @@ fn inner_main() -> Result<ExitCode> {
}

// Extract settings for internal use.
let fix_enabled: bool = configuration.fix;
let autofix = if configuration.fix {
fixer::Mode::Apply
} else if matches!(configuration.format, SerializationFormat::Json) {
fixer::Mode::Generate
} else {
fixer::Mode::None
};
let settings = Settings::from_configuration(configuration, project_root.as_ref())?;

// Now that we've inferred the appropriate log level, add some debug
Expand Down Expand Up @@ -299,10 +316,7 @@ fn inner_main() -> Result<ExitCode> {

let printer = Printer::new(&settings.format, &log_level);
if cli.watch {
if settings.format != SerializationFormat::Text {
eprintln!("Warning: --format 'text' is used in watch mode.");
}
if fix_enabled {
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
eprintln!("Warning: --fix is not enabled in watch mode.");
}
if cli.add_noqa {
Expand All @@ -311,12 +325,15 @@ fn inner_main() -> Result<ExitCode> {
if cli.autoformat {
eprintln!("Warning: --autoformat is not enabled in watch mode.");
}
if settings.format != SerializationFormat::Text {
eprintln!("Warning: --format 'text' is used in watch mode.");
}

// Perform an initial run instantly.
printer.clear_screen()?;
printer.write_to_user("Starting linter in watch mode...\n");

let messages = run_once(&cli.files, &settings, cache_enabled, false);
let messages = run_once(&cli.files, &settings, cache_enabled, &fixer::Mode::None);
printer.write_continuously(&messages)?;

// Configure the file watcher.
Expand All @@ -334,7 +351,8 @@ fn inner_main() -> Result<ExitCode> {
printer.clear_screen()?;
printer.write_to_user("File change detected...\n");

let messages = run_once(&cli.files, &settings, cache_enabled, false);
let messages =
run_once(&cli.files, &settings, cache_enabled, &fixer::Mode::None);
printer.write_continuously(&messages)?;
}
}
Expand All @@ -359,15 +377,15 @@ fn inner_main() -> Result<ExitCode> {
let diagnostics = if is_stdin {
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
let path = Path::new(&filename);
run_once_stdin(&settings, path, fix_enabled)?
run_once_stdin(&settings, path, &autofix)?
} else {
run_once(&cli.files, &settings, cache_enabled, fix_enabled)
run_once(&cli.files, &settings, cache_enabled, &autofix)
};

// Always try to print violations (the printer itself may suppress output),
// unless we're writing fixes via stdin (in which case, the transformed
// source code goes to stdout).
if !(is_stdin && fix_enabled) {
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
printer.write_once(&diagnostics)?;
}

Expand Down
3 changes: 3 additions & 0 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};

use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckKind};
use crate::source_code_locator::SourceCodeLocator;

Expand All @@ -12,6 +13,7 @@ pub struct Message {
pub kind: CheckKind,
pub location: Location,
pub end_location: Location,
pub fix: Option<Fix>,
pub filename: String,
pub source: Option<Source>,
}
Expand All @@ -22,6 +24,7 @@ impl Message {
kind: check.kind,
location: Location::new(check.location.row(), check.location.column() + 1),
end_location: Location::new(check.end_location.row(), check.end_location.column() + 1),
fix: check.fix,
filename,
source,
}
Expand Down
7 changes: 4 additions & 3 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use itertools::iterate;
use rustpython_parser::ast::Location;
use serde::Serialize;

use crate::checks::{CheckCode, CheckKind};
use crate::autofix::Fix;
use crate::checks::CheckCode;
use crate::fs::relativize_path;
use crate::linter::Diagnostics;
use crate::logging::LogLevel;
Expand All @@ -19,9 +20,9 @@ use crate::tell_user;

#[derive(Serialize)]
struct ExpandedMessage<'a> {
kind: &'a CheckKind,
code: &'a CheckCode,
message: String,
fix: Option<&'a Fix>,
location: Location,
end_location: Location,
filename: &'a str,
Expand Down Expand Up @@ -85,9 +86,9 @@ impl<'a> Printer<'a> {
.messages
.iter()
.map(|message| ExpandedMessage {
kind: &message.kind,
code: message.kind.code(),
message: message.kind.body(),
fix: message.fix.as_ref(),
location: message.location,
end_location: message.end_location,
filename: &message.filename,
Expand Down
32 changes: 30 additions & 2 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ fn test_stdin_error() -> Result<()> {
.write_stdin("import os\n")
.assert()
.failure();
assert!(str::from_utf8(&output.get_output().stdout)?.contains("-:1:8: F401"));
assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"Found 1 error(s).\n-:1:8: F401 `os` imported but unused\n1 potentially fixable with the \
--fix option.\n"
);
Ok(())
}

Expand All @@ -33,7 +37,31 @@ fn test_stdin_filename() -> Result<()> {
.write_stdin("import os\n")
.assert()
.failure();
assert!(str::from_utf8(&output.get_output().stdout)?.contains("F401.py:1:8: F401"));
assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"Found 1 error(s).\nF401.py:1:8: F401 `os` imported but unused\n1 potentially fixable \
with the --fix option.\n"
);
Ok(())
}

#[test]
fn test_stdin_json() -> Result<()> {
let mut cmd = Command::cargo_bin(crate_name!())?;
let output = cmd
.args(["-", "--format", "json", "--stdin-filename", "F401.py"])
.write_stdin("import os\n")
.assert()
.failure();
assert_eq!(
str::from_utf8(&output.get_output().stdout)?,
"[\n {\n \"code\": \"F401\",\n \"message\": \"`os` imported but unused\",\n \
\"fix\": {\n \"content\": \"\",\n \"location\": {\n \"row\": 1,\n \
\"column\": 0\n },\n \"end_location\": {\n \"row\": 2,\n \
\"column\": 0\n }\n },\n \"location\": {\n \"row\": 1,\n \
\"column\": 8\n },\n \"end_location\": {\n \"row\": 1,\n \"column\": \
10\n },\n \"filename\": \"F401.py\"\n }\n]\n"
);
Ok(())
}

Expand Down

0 comments on commit 46f5053

Please sign in to comment.