-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Move RDJSON rendering to ruff_db
#19293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
| // The schema says this is optional, so we may be able to skip the preview check. | ||
| if range.is_none() && !config.preview { | ||
| range = Some(RdjsonRange::default()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main preview check I think we could skip, based on this definition of Location:
As I mention in the module comment, only the description field says "Optional," not the type field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest using a schema validator to validate our understanding, e.g. https://www.jsonschemavalidator.net/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A null range is invalid according to the validator, so this should just be unwrap_or_default irrespective of preview.
Actually, completely omitting it from the JSON also passes the validator, so I guess another option is skipping serialization if it's None.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, we should skip it then (which should be easy enough with serde)
| message: diagnostic.body(), | ||
| location: RdjsonLocation { path, range }, | ||
| code: RdjsonCode { | ||
| value: diagnostic.secondary_code(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is actually not optional in the schema, but this is consistent with main. I guess we may need to unwrap_or_default here. The same might be true for url. Both are currently null for syntax errors:
ruff/crates/ruff/tests/snapshots/lint__output_format_rdjson.snap
Lines 77 to 80 in 7154b64
| "code": { | |
| "url": null, | |
| "value": null | |
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion here is to use the lint id if the secondary code is missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, updated! The validator is also complaining about the URL. I guess I'll just put the CARGO_PKG_HOMEPAGE if there's no rule-specific page available.
| name: "ruff", | ||
| url: env!("CARGO_PKG_HOMEPAGE"), | ||
| }, | ||
| severity: "warning", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also consistent with main. We could use our real severity here, but it would change the default from warning to error for existing Ruff diagnostics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest making this change under preview
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that this is a single severity for the whole collection. I guess we can use the max severity in preview.
There's also a severity field on individual diagnostics that we aren't using. I'll try that in the schema validator too.
this being optional was violating the schema. see https://www.jsonschemavalidator.net/s/uvGWl3nq
|
@ntBre is this ready for re-review or are there more changes that you plan on making (I have no particular reason to believe that there's anything that needs changing. I just want to avoid taking a look if you plan on making changes to due the schema discussion we had) |
|
Yes it's ready again, I just added your suggestion about skipping the |
| // The schema does _not_ say this is optional, so always unwrap to a default value. | ||
| let path = span | ||
| .map(|span| span.file().path(resolver)) | ||
| .unwrap_or_default(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
location is also an additional property. Can you verify if the JSON is valid if you omit the location?
If it can be omitted, we then shouldn't call back to unwrap_or_default and instead omit the location when a file is missing (a diagnostic should never have just a range because a range without a file is not very useful)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's valid to omit the location. I'll do that instead.
| .map(|span| span.file().path(resolver)) | ||
| .unwrap_or_default(); | ||
|
|
||
| let severity = if config.preview { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep setting WARNING for all diagnostics until we officially support a WARNING severity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We weren't setting the per-diagnostic severity at all before. Would it be better to go back to that, or to set them all to WARNING?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably remove it for now. We can add the severity when introducing a warning severity to ruff
| url: diagnostic | ||
| .to_ruff_url() | ||
| .unwrap_or_else(|| env!("CARGO_PKG_HOMEPAGE").to_string()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to go with what they have in ther protobuf implementation where, as far as I can understand, it's valid to omit the url property when empty
I'm inclined to go with that for now (the same with the range on location)
Even their own tests don't specify a url: https://github.com/reviewdog/reviewdog/blob/master/parser/rdjson_test.go
| fn rdjson_suggestions<'a>( | ||
| edits: &'a [Edit], | ||
| source_code: Option<SourceCode>, | ||
| ) -> Vec<RdjsonSuggestion<'a>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add an assertion to Diagnostic::set_fix instead that asserts that it has an associated source files. Edits without a source file aren't very useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a debug_assert! and then updated some tests that were actually setting fixes on diagnostics without files. Happy to upgrade to assert if you prefer.
| // TODO(brent) this can be required after it's out of preview. | ||
| #[serde(skip_serializing_if = "Option::is_none")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what the benefit is of setting the severity at the file level. I'm also inclined to just keep this None until we add a WARNING severity. Adding severities feels like an unrelated change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah okay, I'll revert the per-diagnostic severity and just keep WARNING as the global severity. I agree that this feels unrelated to the rest of the refactor.
|
I think this should be ready for another look. The main thing I'm unsure about is the |
MichaReiser
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you.
| ], | ||
| "message": null | ||
| }, | ||
| "fix": null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any other test for edits? If not, I think it's worth preserving testing fixes in some test or another.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the output and notebook_output unit tests and the CLI integration tests all have fixes/edits.
| let location = span | ||
| .map(|span| span.file().path(resolver)) | ||
| .map(|path| RdjsonLocation { path, range }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Could we use source_code.name() here? It makes it clearer that range depends on the same value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the risk of missing something obvious here, I don't see a SourceCode::name method. I think the SourceFile has the name but not the SourceCode. The Span seems like the common ancestor for both range and location, unless I factored out a shared span.file() call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it's SourceCode and not SourceFile...
I would rewrite this to
let source_file = span.map(|span| {
let file = span.file();
(file.path(resolver), file.diagnostic_source(resolver))
});
let location = source_file.as_ref().map(|(path, source)| {
let range = diagnostic.range().map(|range| {
let source_code = source.as_source_code();
let start = source_code.line_column(range.start());
let end = source_code.line_column(range.end());
RdjsonRange::new(start, end)
});
RdjsonLocation { path, range }
});
let edits = diagnostic.fix().map(Fix::edits).unwrap_or_default();
RdjsonDiagnostic {
message: diagnostic.body(),
location,
code: RdjsonCode {
value: diagnostic
.secondary_code()
.map_or_else(|| diagnostic.name(), |code| code.as_str()),
url: diagnostic.to_ruff_url(),
},
suggestions: rdjson_suggestions(edits, source_file.map(|(_, source)| source)),
}It makes it clearer that range can only be set when span isn't None.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I like this better than the version in this PR with many separate map calls and also better than the mutable version in #19340. I'll try to use this pattern going forward.
| .map(|edit| { | ||
| // Safety: we assert that diagnostics with fixes have an associated source file in | ||
| // `Diagnostic::set_fix`. | ||
| let source_code = source_code.as_ref().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: You could make this code more error resilient by skipping all edits if source_code is empty (but use a debug_assert to make it fail if that happens).
It has the benefit that Ruff doesn't panic if we ever fail to set the source file
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Micha Reiser <micha@reiser.io>
* main: [`pylint`] Extend invalid string character rules to include t-strings (#19355) Make TC010 docs example more realistic (#19356) Move RDJSON rendering to `ruff_db` (#19293) [`flake8-use-pathlib`] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 (#19331) [`ruff`] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ (#19333) [ty] Reduce false positives for `TypedDict` types (#19354) [ty] Remove `ConnectionInitializer` (#19353) [ty] Use `Type::string_literal()` more (#19352) [ty] Add ecosystem-report workflow (#19349) [ty] Make use of salsa `Lookup` when interning values (#19347) [ty] Sync vendored typeshed stubs (#19345) [`pylint`] Make example error out-of-the-box (`PLE2502`) (#19272) [`pydoclint`] Fix `SyntaxError` from fixes with line continuations (`D201`, `D202`) (#19246)
* dcreager/merge-arguments: add types iterator add asserting constructor debug assert lengths remove unused From use FromIterator [`pylint`] Extend invalid string character rules to include t-strings (#19355) Make TC010 docs example more realistic (#19356) Move RDJSON rendering to `ruff_db` (#19293) [`flake8-use-pathlib`] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 (#19331) [`ruff`] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ (#19333) [ty] Reduce false positives for `TypedDict` types (#19354) [ty] Remove `ConnectionInitializer` (#19353) [ty] Use `Type::string_literal()` more (#19352) [ty] Add ecosystem-report workflow (#19349) [ty] Make use of salsa `Lookup` when interning values (#19347) [ty] Sync vendored typeshed stubs (#19345) [`pylint`] Make example error out-of-the-box (`PLE2502`) (#19272) [`pydoclint`] Fix `SyntaxError` from fixes with line continuations (`D201`, `D202`) (#19246)
…finition * 'main' of https://github.com/astral-sh/ruff: (39 commits) [ty] Sync vendored typeshed stubs (astral-sh#19368) Fix typeshed-sync workflow (astral-sh#19367) Rework typeshed-sync workflow to also add docstrings for Windows- and MacOS-specific APIs (astral-sh#19360) [ty] Allow `-qq` for silent output mode (astral-sh#19366) [ty] Allow `-q` short alias for `--quiet` (astral-sh#19364) Add shellcheck to pre-commit (astral-sh#19361) distinguish references from definitions in `infer_nonlocal` [`pycodestyle`] Handle brace escapes for t-strings in logical lines (astral-sh#19358) [ty] Combine CallArguments and CallArgumentTypes (astral-sh#19337) Move Pylint rendering to `ruff_db` (astral-sh#19340) [`pylint`] Extend invalid string character rules to include t-strings (astral-sh#19355) Make TC010 docs example more realistic (astral-sh#19356) Move RDJSON rendering to `ruff_db` (astral-sh#19293) [`flake8-use-pathlib`] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 (astral-sh#19331) [`ruff`] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ (astral-sh#19333) [ty] Reduce false positives for `TypedDict` types (astral-sh#19354) [ty] Remove `ConnectionInitializer` (astral-sh#19353) [ty] Use `Type::string_literal()` more (astral-sh#19352) [ty] Add ecosystem-report workflow (astral-sh#19349) [ty] Make use of salsa `Lookup` when interning values (astral-sh#19347) ... # Conflicts: # crates/ty_ide/src/goto.rs # crates/ty_server/src/server.rs
Summary
Another output format like #19133. This is the reviewdog output format, which is somewhat similar to regular JSON. Like #19270, in the first commit I converted from using
json!toSerializestructs, then in the second commit I moved the module toruff_db.The reviewdog schema seems a bit more flexible than our JSON schema, so I'm not sure if we need any preview checks here. I'll flag the places I wasn't sure about as review comments.
Test Plan
New tests in
rdjson.rs, ported from the oldrjdson.rsmodule, as well as the new CLI output tests.