Skip to content
Merged
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
3 changes: 2 additions & 1 deletion crates/ruff/src/commands/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ pub(crate) fn check(
if let Ok(ResolvedFile::Root(path) | ResolvedFile::Nested(path)) = path {
matches!(
SourceType::from(path),
SourceType::Python(_) | SourceType::Toml(TomlSourceType::Pyproject)
SourceType::Python(_)
| SourceType::Toml(TomlSourceType::Pyproject | TomlSourceType::Ruff)
)
} else {
true
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/commands/show_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pub(crate) fn show_files(
if let Ok(ResolvedFile::Root(path) | ResolvedFile::Nested(path)) = path {
matches!(
SourceType::from(path),
SourceType::Python(_) | SourceType::Toml(TomlSourceType::Pyproject)
SourceType::Python(_)
| SourceType::Toml(TomlSourceType::Pyproject | TomlSourceType::Ruff)
)
} else {
true
Expand Down
103 changes: 87 additions & 16 deletions crates/ruff/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use ruff_db::diagnostic::Diagnostic;
use ruff_linter::codes::Rule;
use ruff_linter::linter::{FixTable, FixerResult, LinterResult, ParseSource, lint_fix, lint_only};
use ruff_linter::package::PackageRoot;
use ruff_linter::pyproject_toml::lint_pyproject_toml;
use ruff_linter::settings::types::UnsafeFixes;
use ruff_linter::settings::{LinterSettings, flags};
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_linter::source_kind::{SourceError, SourceKind, SourceKindDiff};
use ruff_linter::toml::{TomlFixerResult, lint_fix_toml, lint_toml};
use ruff_linter::{IOError, Violation, fs};
use ruff_notebook::{NotebookError, NotebookIndex};
use ruff_python_ast::{SourceType, TomlSourceType};
Expand Down Expand Up @@ -213,25 +213,59 @@ pub(crate) fn lint_path(
debug!("Checking: {}", path.display());

let source_type = match settings.extension.get_source_type(path) {
SourceType::Toml(TomlSourceType::Pyproject) => {
let diagnostics = if settings
SourceType::Toml(source_type @ (TomlSourceType::Pyproject | TomlSourceType::Ruff)) => {
let (diagnostics, fixed) = if settings
.rules
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
.any(|rule_code| rule_code.lint_source().is_toml())
{
let contents = match std::fs::read_to_string(path).map_err(SourceError::from) {
Ok(contents) => contents,
Err(err) => {
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
}
};
lint_pyproject_toml(path, &contents, settings)
if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
let TomlFixerResult {
diagnostics,
transformed,
fixed,
} = lint_fix_toml(path, &contents, settings, source_type, unsafe_fixes);

if !fixed.is_empty() {
match fix_mode {
flags::FixMode::Apply => {
File::create(path)?.write_all(transformed.as_bytes())?;
}
flags::FixMode::Diff => {
write!(
&mut io::stdout().lock(),
"{}",
SourceKindDiff::from_text(
&contents,
transformed.as_ref(),
Some(path),
)
)?;
}
flags::FixMode::Generate => {}
}
}

(diagnostics, fixed)
} else {
(
lint_toml(path, &contents, settings, source_type),
FixTable::default(),
)
}
} else {
vec![]
(vec![], FixTable::default())
};
return Ok(Diagnostics {
inner: diagnostics,
..Diagnostics::default()
fixed: FixMap::from_iter([(fs::relativize_path(path), fixed)]),
notebook_indexes: FxHashMap::default(),
});
}
SourceType::Toml(_) | SourceType::Markdown => return Ok(Diagnostics::default()),
Expand Down Expand Up @@ -358,26 +392,63 @@ pub(crate) fn lint_stdin(
.map(|path| settings.linter.extension.get_source_type(path))
.unwrap_or_default()
{
SourceType::Toml(source_type) if source_type.is_pyproject() => {
SourceType::Toml(source_type @ (TomlSourceType::Pyproject | TomlSourceType::Ruff)) => {
if !settings
.linter
.rules
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
.any(|rule_code| rule_code.lint_source().is_toml())
{
return Ok(Diagnostics::default());
}

let path = path.unwrap();

match fix_mode {
flags::FixMode::Diff | flags::FixMode::Generate => {}
flags::FixMode::Apply => write!(&mut io::stdout().lock(), "{contents}")?,
}
let (diagnostics, fixed) =
if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
Comment on lines +407 to +408

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we could reuse more with the python code path below. The structure here now feels very similar. I don't think we should push it very hard, but if you see an opportunity to do so

let TomlFixerResult {
diagnostics,
transformed,
fixed,
} = lint_fix_toml(
path,
&contents,
&settings.linter,
source_type,
settings.unsafe_fixes,
);

match fix_mode {
flags::FixMode::Apply => {
write!(&mut io::stdout().lock(), "{transformed}")?;
}
flags::FixMode::Diff => {
if !fixed.is_empty() {
write!(
&mut io::stdout().lock(),
"{}",
SourceKindDiff::from_text(
&contents,
transformed.as_ref(),
Some(path),
)
)?;
}
}
flags::FixMode::Generate => {}
}

(diagnostics, fixed)
} else {
(
lint_toml(path, &contents, &settings.linter, source_type),
FixTable::default(),
)
};

return Ok(Diagnostics {
inner: lint_pyproject_toml(path, &contents, &settings.linter),
fixed: FixMap::from_iter([(fs::relativize_path(path), FixTable::default())]),
inner: diagnostics,
fixed: FixMap::from_iter([(fs::relativize_path(path), fixed)]),
notebook_indexes: FxHashMap::default(),
});
}
Expand Down
40 changes: 39 additions & 1 deletion crates/ruff/tests/cli/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,6 @@ ignore = ["D203", "D212"]
All checks passed!

----- stderr -----
warning: No Python files found under the given path(s)
");

Ok(())
Expand Down Expand Up @@ -5192,3 +5191,42 @@ fn preview_default_rules() -> Result<()> {
);
Ok(())
}

#[test]
fn ruff_toml_is_linted() -> Result<()> {
let test = CliTest::with_file("ruff.toml", r#"lint.select = ["F401"]"#)?;

assert_cmd_snapshot!(
test.command().args([
"check",
"--no-cache",
"--isolated",
"--preview",
"--select",
"RUF201",
]),
@r#"
success: false
exit_code: 1
----- stdout -----
rule-codes-in-selectors: [*] Rule code used instead of name in `lint.select`
--> ruff.toml:1:17
|
1 | lint.select = ["F401"]
| ^^^^
|
help: Replace rule code with `unused-import`
|
- lint.select = ["F401"]
1 + lint.select = ["unused-import"]
|

Found 1 error.
[*] 1 fixable with the `--fix` option.

----- stderr -----
"#,
);

Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ file_resolver.include = [
"*.pyw",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
"*.md",
]
file_resolver.extend_include = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ file_resolver.include = [
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
"**/ruff.toml",
"**/.ruff.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
Expand Down
5 changes: 4 additions & 1 deletion crates/ruff_dev/src/format_dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,10 @@ fn format_dir_entry(
) -> anyhow::Result<(Result<Statistics, CheckFileError>, PathBuf), Error> {
let resolved_file = resolved_file.context("Iterating the files in the repository failed")?;
// For some reason it does not filter in the beginning
if resolved_file.file_name() == "pyproject.toml" {
if ["pyproject.toml", "ruff.toml", ".ruff.toml"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about ruff format?

Codex reports

[P2] Adding Ruff configs to stable discovery breaks formatter range mode. settings.rs:146
With one Python file and ruff.toml, ruff format --check --range 1:1-1:9 DIR now fails because the formatter validates the discovered-file count before filtering TOML. Previously the directory resolved to one formattable file. Filter TOML before the range check or avoid adding these files to stable formatter discovery.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, none of my Codex reviews ever caught this. Possibly because this reproduces on main with pyproject.toml:

$ touch pyproject.toml
$ touch try.py
$ uvx ruff@latest format --range 1:1-1:9 .
ruff failed
  Cause: The `--range` option is only supported when formatting a single file but the specified paths resolve to 2 files.

Is passing a directory to --range meant to be a supported operation?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really... I should have noticed this

.iter()
.any(|&path| resolved_file.file_name() == path)
{
return Ok((Ok(Statistics::default()), resolved_file.into_path()));
}

Expand Down
Loading
Loading