-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[red-knot] Add a test to ensure that KnownClass::try_from_file_and_name()
is kept up to date
#16326
Conversation
|
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.
Could we use strum::EnumString
https://github.com/Peternator7/strum/wiki/Derive-EnumString if we're adding a strum
dependency anyway?
This PR currently only adds strum as a dev-dependency. But I can make that change if we're happy to have the extra dependency in production as well. |
I also played around a bit with a solution to this TODO on the weekend. One thing that makes this more complicated are branches like this, where we have additional logic in the enum->string function: Self::EllipsisType => {
// Exposed as `types.EllipsisType` on Python >=3.10;
// backported as `builtins.ellipsis` by typeshed on Python <=3.9
if Program::get(db).python_version(db) >= PythonVersion::PY310 {
"EllipsisType"
} else {
"ellipsis"
}
} |
There shouldn't really be any difference. |
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 great. Thank you (and sorry for the merge conflict :()
e6e8545
to
7455a2f
Compare
…deriving `EnumIter` fix test and add a similar one for `KnownModule` add a test for `KnownFunction` too Simplify by deriving `EnumString` where possible revert renamings of `KnownFunction` variants
7455a2f
to
59f828e
Compare
Summary
We keep on adding new variants to the
KnownClass
enum but forgetting to update this method, because the Rust compiler only enforces exhaustiveness over enums when the enum variants appear on the left-hand side of thematch
statement:ruff/crates/red_knot_python_semantic/src/types.rs
Lines 3183 to 3231 in b312b53
That leads to subtle and confusing bugs that can be hard to debug, and that sometimes go unnoticed for several weeks.
This PR adds a test that will fail if we add a variant to the
KnownClass
enum and forget to add a case toKnownClass::try_from_file_and_name()
. It also adds tests for the similar methods on theKnownModule
andKnownFunction
enums.How it works
strum
andstrum_macros
are added as test-only dependencies for red-knot (they are added to thedev-dependencies
table in thered_knot_python_semantic
crate'sCargo.toml
). If thetest
feature is enabled, we derive theEnumIter
trait for theKnownClass
enum. In the tests forKnownClass
, we then use the generatediter()
method to iterate over allKnownClass
variants and verify that each variant has its own branch in theKnownClass::try_from_file_and_name()
function.Following review, this has been changed:
strum
andstrum_macros
are now just regular dependencies for red-knot, in production as well as if thetest
feature is enabled.KnownFunction
andKnownModule
, we use theEnumString
macro fromstrum_macros
to simplify the deserialization constructors and ensure that they are exhaustive over the right-hand side. (This unfortunately doesn't work forKnownClass
because of the fact that theEllipsisType
branch deserializes dynamically depending on the target Python version.)EnumIter
macro.Limitations
Unfortunately, this approach doesn't work for the
KnownInstanceType
enum.KnownInstanceType
is generic over a lifetime, and theEnumIter
trait cannot be derived for enums generic over a lifetime. I therefore haven't touched that enum in this PR.This approach also necessitated some small refactoring of the
KnownFunction
enum.EnumIter
andEnumString
couldn't be derived for the enum as-is because of the fact that theConstraintFunction
variant wrapped inner data. I solved this by replacing theConstraintFunction
variant (which wrapped an inner enum) with two variants (KnownFunction::IsSubclass
andKnownFunction::IsInstance
), and slightly reworking theKnownFunction::constraint_function()
method (which is renamed toKnownFunction::into_constraint_function()
)