[ty] Add go-to definition for dataclass/TypedDict/NamedTuple fields from keyword arguments#24378
[ty] Add go-to definition for dataclass/TypedDict/NamedTuple fields from keyword arguments#24378Viicos wants to merge 2 commits intoastral-sh:mainfrom
Conversation
| if resolved_definitions.is_empty() | ||
| && let Some(class_literal) = func_type.as_class_literal() | ||
| && let Some(static_class_literal) = class_literal.as_static() | ||
| && let Some(field_policy) = CodeGeneratorKind::from_class(db, class_literal, None) |
There was a problem hiding this comment.
Note: this is a bit of a leaky abstraction, because it assumes CodeGeneratorKind can only be created for classes that support this goto logic. Maybe we could also guard with:
| && let Some(field_policy) = CodeGeneratorKind::from_class(db, class_literal, None) | |
| && (static_class_literal.is_dataclass_like(db) || static_class_literal.is_typed_dict(db) || static_class_literal.has_named_tuple_class_in_mro(db)) | |
| && let Some(field_policy) = CodeGeneratorKind::from_class(db, class_literal, None) |
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 87.92%. The percentage of expected errors that received a diagnostic held steady at 83.11%. The number of fully passing files held steady at 78/133. |
Memory usage reportMemory usage unchanged ✅ |
| // Go to field definitions if the type is a dataclass-like type, a named tuple or a typed dict. Only do so if no definition was found, | ||
| // as having existing definition(s) would mean a custom constructor is defined and as such takes priority: |
There was a problem hiding this comment.
Actually, I think we should explicitly check if a custom constructor is set, because with the following:
@dataclass
class A:
a: int
def __init__(self, b: int) -> None: ...
A(a<CURSOR>=1)It would still go to the a field. Maybe not worth the added complexity?
|
|
Thank you. I'm not sure this is the right approach as it results in needing multiple special casing:
I'm not entirely sure how to integrate this best. Ideally, we'd do something when resolving the constructor call. @oconnor663 I think you worked on TypedDict. Do you think we could synthesize the constructor function somehow during type inference and if so, where would we add this? |
We already do this -- here for dataclasses: ruff/crates/ty_python_semantic/src/types/class/static_literal.rs Lines 1331 to 1340 in f4c3807 Here for class-based ruff/crates/ty_python_semantic/src/types/class/static_literal.rs Lines 1341 to 1386 in f4c3807 Here for functional namedtuples: ruff/crates/ty_python_semantic/src/types/class/named_tuple.rs Lines 386 to 412 in f4c3807 I'm not sure where TypedDict constructors get synthesized off the top of my head, but there must be a similar hook somewhere in type inference |
|
Yes to clarify, this doesn't require the synthesized signatures to be available at all. The class CustomClass:
def __init__(self, a: int) -> None: ...
# Trigger go-to on a keyworg argument will go to `a` parameter in `CustomClass.__init__()`:
CustomClass(a=1)For synthesized methods, this condition in ruff/crates/ty_python_semantic/src/types/ide_support.rs Lines 528 to 530 in d85aac2 is never reached because synthesized constructors parameters don't have any range. We end up with an empty |
83a6ef7 to
fa7784d
Compare
|
That makes sense. I still think it would be nice to avoid special casing typed dict in the IDE code and instead expose the necessary information in synthesized constructors. For example, what if we add a |
|
Tracking an optional definition with |
|
I went ahead and implemented my described approach in #24897 |
Fixes astral-sh/ty#3207.
Also fixes a small unrelated typo.
Summary
Test Plan