diff --git a/crates/ruff/src/diagnostics.rs b/crates/ruff/src/diagnostics.rs index b61eea5880..fc462de30f 100644 --- a/crates/ruff/src/diagnostics.rs +++ b/crates/ruff/src/diagnostics.rs @@ -367,8 +367,7 @@ pub(crate) fn lint_path( }) } -/// Generate `Diagnostic`s from source code content derived from -/// stdin. +/// Generate `Diagnostic`s from source code content derived from stdin. pub(crate) fn lint_stdin( path: Option<&Path>, package: Option>, @@ -377,13 +376,37 @@ pub(crate) fn lint_stdin( noqa: flags::Noqa, fix_mode: flags::FixMode, ) -> Result { - // TODO(charlie): Support `pyproject.toml`. let source_type = match path.and_then(|path| settings.linter.extension.get(path)) { None => match path.map(SourceType::from).unwrap_or_default() { SourceType::Python(source_type) => source_type, - SourceType::Toml(_) => { - return Ok(Diagnostics::default()); + + SourceType::Toml(source_type) if source_type.is_pyproject() => { + if !settings + .linter + .rules + .iter_enabled() + .any(|rule_code| rule_code.lint_source().is_pyproject_toml()) + { + return Ok(Diagnostics::default()); + } + + let path = path.unwrap(); + let source_file = + SourceFileBuilder::new(path.to_string_lossy(), contents.clone()).finish(); + + match fix_mode { + flags::FixMode::Diff | flags::FixMode::Generate => {} + flags::FixMode::Apply => write!(&mut io::stdout().lock(), "{contents}")?, + } + + return Ok(Diagnostics { + messages: lint_pyproject_toml(source_file, &settings.linter), + fixed: FixMap::from_iter([(fs::relativize_path(path), FixTable::default())]), + notebook_indexes: FxHashMap::default(), + }); } + + SourceType::Toml(_) => return Ok(Diagnostics::default()), }, Some(language) => PySourceType::from(language), }; diff --git a/crates/ruff/tests/integration_test.rs b/crates/ruff/tests/integration_test.rs index ae5d850da3..3e8f5497ed 100644 --- a/crates/ruff/tests/integration_test.rs +++ b/crates/ruff/tests/integration_test.rs @@ -2233,3 +2233,195 @@ unfixable = ["RUF"] Ok(()) } + +#[test] +fn pyproject_toml_stdin_syntax_error() { + let mut cmd = RuffCheck::default() + .args(["--stdin-filename", "pyproject.toml", "--select", "RUF200"]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project"), + @r" + success: false + exit_code: 1 + ----- stdout ----- + pyproject.toml:1:9: RUF200 Failed to parse pyproject.toml: invalid table header + expected `.`, `]` + | + 1 | [project + | ^ RUF200 + | + + Found 1 error. + + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_schema_error() { + let mut cmd = RuffCheck::default() + .args(["--stdin-filename", "pyproject.toml", "--select", "RUF200"]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project]\nname = 1"), + @r" + success: false + exit_code: 1 + ----- stdout ----- + pyproject.toml:2:8: RUF200 Failed to parse pyproject.toml: invalid type: integer `1`, expected a string + | + 1 | [project] + 2 | name = 1 + | ^ RUF200 + | + + Found 1 error. + + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_no_applicable_rules_selected() { + let mut cmd = RuffCheck::default() + .args(["--stdin-filename", "pyproject.toml"]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project"), + @r" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_no_applicable_rules_selected_2() { + let mut cmd = RuffCheck::default() + .args(["--stdin-filename", "pyproject.toml", "--select", "F401"]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project"), + @r" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_no_errors() { + let mut cmd = RuffCheck::default() + .args(["--stdin-filename", "pyproject.toml"]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin(r#"[project]\nname = "ruff"\nversion = "0.0.0""#), + @r" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_schema_error_fix() { + let mut cmd = RuffCheck::default() + .args([ + "--stdin-filename", + "pyproject.toml", + "--select", + "RUF200", + "--fix", + ]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project]\nname = 1"), + @r" + success: false + exit_code: 1 + ----- stdout ----- + [project] + name = 1 + ----- stderr ----- + pyproject.toml:2:8: RUF200 Failed to parse pyproject.toml: invalid type: integer `1`, expected a string + | + 1 | [project] + 2 | name = 1 + | ^ RUF200 + | + + Found 1 error. + " + ); +} + +#[test] +fn pyproject_toml_stdin_schema_error_fix_only() { + let mut cmd = RuffCheck::default() + .args([ + "--stdin-filename", + "pyproject.toml", + "--select", + "RUF200", + "--fix-only", + ]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project]\nname = 1"), + @r" + success: true + exit_code: 0 + ----- stdout ----- + [project] + name = 1 + ----- stderr ----- + " + ); +} + +#[test] +fn pyproject_toml_stdin_schema_error_fix_diff() { + let mut cmd = RuffCheck::default() + .args([ + "--stdin-filename", + "pyproject.toml", + "--select", + "RUF200", + "--fix", + "--diff", + ]) + .build(); + + assert_cmd_snapshot!( + cmd.pass_stdin("[project]\nname = 1"), + @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + " + ); +}