Create Unknown rule diagnostics with a source range#15648
Conversation
e1d941a to
10b8841
Compare
|
| let name = project | ||
| .and_then(|project| project.name.as_ref()) | ||
| .map(|name| Name::new(&**name)) | ||
| .map(|name| Name::new(&***name)) |
There was a problem hiding this comment.
How long before it becomes &********* 😆
| /// | ||
| /// If a rule isn't present in this map, then it should be considered disabled. | ||
| lints: FxHashMap<LintId, Severity>, | ||
| lints: FxHashMap<LintId, (Severity, LintSource)>, |
There was a problem hiding this comment.
This is not strictly necessary for what we need today but the intent here is that we show a note in diagnostics similar to clippy, telling users why a check was reported (you enabled this rule in the configuration, you enabled it in the cli, it's enabled by default, etc)
| pub struct EnvironmentOptions { | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub python_version: Option<PythonVersion>, | ||
| pub python_version: Option<RangedValue<PythonVersion>>, |
There was a problem hiding this comment.
For now, it would be sufficient to only make the key in the Rules table ranged because we don't use the range or source for any other value. I instead opted to make all values Ranged because the overhead should be small and having the range will make it easier to write good diagnostics.
a6bf876 to
5fa96e4
Compare
| let name = project | ||
| .and_then(|project| project.name.as_ref()) | ||
| .map(|name| Name::new(&**name)) | ||
| .map(|name| Name::new(&***name)) |
| /// The byte range of `value` in `source`. | ||
| /// | ||
| /// Can be `None` because not all sources support a range. | ||
| /// For example, arguments provided on the CLI won't have a range attached. | ||
| range: Option<TextRange>, |
There was a problem hiding this comment.
This allows representing the invalid states where a value with CLI source has Some range, or a value with file source has None range. What would be the tradeoffs of instead recording the range as part of the ValueSource? (I realize this would mean splitting the ValueSource type used here from the ValueSource type stored in thread-locals, but that doesn't necessarily seem like a problem).
There was a problem hiding this comment.
I considered that but decided against it for two reasons:
- It doesn't simplify downstream code because no code depends on the enforced constraint. However, enforcing it here does require more code because we'd need a
ValueSourceKind(which is what we set in the thread local) andValueSource. - We'll need to deserialize a
serde::Valuewhen supporting more complex types from the CLI using--config complex.key = "{ sub = [... ] }"and thetomlcrate can't emit text ranges in that case. I'm not sure if we have a similar case for configurations coming from a file, but maybe when working on the uv integration?
Overall, there are too many unknowns for cases where we might not have a range even if the value comes from a file and there are no "downstream" improvements to enforce the constraint which is why I'd prefer to keep it as is for now.
| impl Combine for RelativePathBuf { | ||
| fn combine(self, _other: Self) -> Self { | ||
| self | ||
| // The type already has a `into_iter` method thanks to `Deref`. |
There was a problem hiding this comment.
This comment doesn't make sense to me, because we are defining the into_iter method for the RangedValue type here. Did you mean to say that it already has an iter method?
0bf7453 to
3a4b7f0
Compare
5fa96e4 to
997bd03
Compare
* main: [red-knot] MDTests: Do not depend on precise public-symbol type inference (#15691) [red-knot] Make `infer.rs` unit tests independent of public symbol inference (#15690) Tidy knot CLI tests (#15685) [red-knot] Port comprehension tests to Markdown (#15688) Create Unknown rule diagnostics with a source range (#15648) [red-knot] Port 'deferred annotations' unit tests to Markdown (#15686) [red-knot] Support custom typeshed Markdown tests (#15683) Don't run the linter ecosystem check on PRs that only touch red-knot crates (#15687) Add `rules` table to configuration (#15645) [red-knot] Make `Diagnostic::file` optional (#15640) [red-knot] Add test for nested attribute access (#15684) [red-knot] Anchor relative paths in configurations (#15634) [`pyupgrade`] Handle multiple base classes for PEP 695 generics (`UP046`) (#15659) [`pyflakes`] Treat arguments passed to the `default=` parameter of `TypeVar` as type expressions (`F821`) (#15679) Upgrade zizmor to the latest version in CI (#15649) [`pyupgrade`] Add rules to use PEP 695 generics in classes and functions (`UP046`, `UP047`) (#15565) [red-knot] Ensure a gradual type can always be assigned to itself (#15675)
Summary
Track the source range of values deserialized from the TOML configuration and use the source range to create unknown-rule diagnostics that point to the right location in the configuration file.
This PR makes use of
toml's spanned functionality to get a text range for every deserialized value in combination with theValueSourcethat I introduced in previ.Toml's spanned feature has one significant drawback: It doesn't support
flattenoruntagged, at least when using the derived deserializer. Thetoml-spancrate explains why in more detail.I considered using
toml-spaninstead, but it requires you to write allDeserializers manually. I ultimately decided to go withtoml's span because:toml-spanif we have to useflattenor similar (either for the entire configuration or only the problematic values)Deserializerdivergeflattenas much as possible because it leads to very unhelpful error messages when the configuration can't be deserialized because of an unknown field or invalid type.I do expect that using this approach will require some hackery to support e.g. a
string | vec<string>value but this has been solved by cargo-vet.This PR is inspired by mozilla/cargo-vet#230
Part of astral-sh/ty#219
Test Plan
See the changes in the CLI test