[ty] print diagnostics with fully qualified name to disambiguate some cases#19850
[ty] print diagnostics with fully qualified name to disambiguate some cases#19850carljm merged 17 commits intoastral-sh:mainfrom
Conversation
Diagnostic diff on typing conformance testsNo changes detected when running ty on typing conformance tests ✅ |
|
|
It seems there are some false positives, I'll work on those later. EDIT: it was an important check that I accidentally deleted while refactoring |
ead13fa to
95f8709
Compare
AlexWaygood
left a comment
There was a problem hiding this comment.
Thank you! A few quick things I noticed:
|
|
||
| impl Display for QualifiedDisplayType<'_> { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
| match self.ty { |
There was a problem hiding this comment.
I'd also add branches for GenericAlias, ProtocolInstance, SubclassOf and TypedDictType here
There was a problem hiding this comment.
Was this that you had in mind?
4cca52e to
d7c01a8
Compare
… cases Example: import pandas import polars df: pandas.DataFrame = polars.DataFrame() # error: [invalid-assignment] "Object of type `polars.DataFrame` is not assignable to `pandas.DataFrame`"
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
d7c01a8 to
68ee987
Compare
|
I had to rebase to solve conflicts, but I think I addressed all the suggestions. I tried doing some testing to the |
carljm
left a comment
There was a problem hiding this comment.
Thank you for working on this!
Sorry for coming in on the late side with a new suggestion, but I think we should take a slightly different approach in the display.rs part of the implementation. Rather than introducing a totally separate QualifiedDisplayType struct, I think we should have a qualified: bool attribute on the existing DisplayType and `DisplayRepresentation structs, which we can respect whenever we are emitting the name of a class or function. (The current PR doesn't seem to handle functions, and it doesn't need to be done in this PR, but at some point I think we'll want to do this for function names, too.) I think this will make it much easier to share parts of the implementation that are not related to whether or not names are qualified, without code duplication.
The qualified flag could be set using a builder pattern, like ty.display(db).qualified() vs just ty.display(db) for the un-qualified display of a type. Though we may also want some form that allows easily passing a boolean value; this will be convenient for when we want to pass along the "qualified" value to display of a nested inner type.
The recently-merged implementation of a "multiline" display option (which probably needs conflict resolution with this PR anyway) is a good model to follow here.
crates/ty_python_semantic/resources/mdtest/assignment/annotations.md
Outdated
Show resolved
Hide resolved
6a2c369 to
a449874
Compare
* main: [`ruff`] Preserve relative whitespace in multi-line expressions (`RUF033`) (astral-sh#19647) [ty] Optimize TDD atom ordering (astral-sh#20098) [`airflow`] Extend `AIR311` and `AIR312` rules (astral-sh#20082) [ty] Preserve qualifiers when accessing attributes on unions/intersections (astral-sh#20114) [ty] Fix the inferred interface of specialized generic protocols (astral-sh#19866) [ty] Infer slightly more precise types for comprehensions (astral-sh#20111) [ty] Add more tests for protocols (astral-sh#20095) [ty] don't eagerly unpack aliases in user-authored unions (astral-sh#20055) [`flake8-use-pathlib`] Update links to the table showing the correspondence between `os` and `pathlib` (astral-sh#20103) [`flake8-use-pathlib`] Make `PTH100` fix unsafe because it can change behavior (astral-sh#20100) [`flake8-use-pathlib`] Delete unused `Rule::OsSymlink` enabled check (astral-sh#20099) [ty] Add search paths info to unresolved import diagnostics (astral-sh#20040) [`flake8-logging-format`] Add auto-fix for f-string logging calls (`G004`) (astral-sh#19303) Add a `ScopeKind` for the `__class__` cell (astral-sh#20048) Fix incorrect D413 links in docstrings convention FAQ (astral-sh#20089) [ty] Refactor inlay hints structure to use separate parts (astral-sh#20052)
carljm
left a comment
There was a problem hiding this comment.
Looking good! I think we should increase our test coverage a bit, and try to reduce code boilerplate.
I think ultimately in order to support nested types we will probably need to change the settings from just having a qualified boolean, to instead having a vector of types whose names should be qualified, so that we can identify the right types to qualify even in nested display. But this can be a separate PR.
carljm
left a comment
There was a problem hiding this comment.
I went ahead and made those updates, this looks good to go!
* main: Fix mdtest ignore python code blocks (#20139) [ty] add support for cyclic legacy generic protocols (#20125) [ty] add cycle detection for find_legacy_typevars (#20124) Use new diff rendering format in tests (#20101) [ty] Fix 'too many cycle iterations' for unions of literals (#20137) [ty] No boundness analysis for implicit instance attributes (#20128) Bump 0.12.11 (#20136) [ty] Benchmarks for problematic implicit instance attributes cases (#20133) [`pyflakes`] Fix `allowed-unused-imports` matching for top-level modules (`F401`) (#20115) Move GitLab output rendering to `ruff_db` (#20117) [ty] Evaluate reachability of non-definitely-bound to Ambiguous (#19579) [ty] Introduce a representation for the top/bottom materialization of an invariant generic (#20076) [`flake8-async`] Implement `blocking-http-call-httpx` (`ASYNC212`) (#20091) [ty] print diagnostics with fully qualified name to disambiguate some cases (#19850) [`ruff`] Preserve relative whitespace in multi-line expressions (`RUF033`) (#19647)
… cases (astral-sh#19850) There are some situations that we have a confusing diagnostics due to identical class names. ## Class with same name from different modules ```python import pandas import polars df: pandas.DataFrame = polars.DataFrame() ``` This yields the following error: **Actual:** error: [invalid-assignment] "Object of type `DataFrame` is not assignable to `DataFrame`" **Expected**: error: [invalid-assignment] "Object of type `polars.DataFrame` is not assignable to `pandas.DataFrame`" ## Nested classes ```python from enum import Enum class A: class B(Enum): ACTIVE = "active" INACTIVE = "inactive" class C: class B(Enum): ACTIVE = "active" INACTIVE = "inactive" ``` **Actual**: error: [invalid-assignment] "Object of type `Literal[B.ACTIVE]` is not assignable to `B`" **Expected**: error: [invalid-assignment] "Object of type `Literal[my_module.C.B.ACTIVE]` is not assignable to `my_module.A.B`" ## Solution In this MR we added an heuristics to detect when to use a fully qualified name: - There is an invalid assignment and; - They are two different classes and; - They have the same name The fully qualified name always includes: - module name - nested classes name - actual class name There was no `QualifiedDisplay` so I had to implement it from scratch. I'm very new to the codebase, so I might have done things inefficiently, so I appreciate feedback. Should we pre-compute the fully qualified name or do it on demand? ## Not implemented ### Function-local classes Should we approach this in a different PR? **Example**: ```python # t.py from __future__ import annotations def function() -> A: class A: pass return A() class A: pass a: A = function() ``` #### mypy ```console t.py:8: error: Incompatible return value type (got "t.A@5", expected "t.A") [return-value] ``` From my testing the 5 in `A@5` comes from the like number. #### ty ```console error[invalid-return-type]: Return type does not match returned value --> t.py:4:19 | 4 | def function() -> A: | - Expected `A` because of return type 5 | class A: 6 | pass 7 | 8 | return A() | ^^^ expected `A`, found `A` | info: rule `invalid-return-type` is enabled by default ``` Fixes astral-sh/ty#848 --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Carl Meyer <carl@astral.sh>
There are some situations that we have a confusing diagnostics due to identical class names.
Class with same name from different modules
This yields the following error:
Actual:
error: [invalid-assignment] "Object of type
DataFrameis not assignable toDataFrame"Expected:
error: [invalid-assignment] "Object of type
polars.DataFrameis not assignable topandas.DataFrame"Nested classes
Actual:
error: [invalid-assignment] "Object of type
Literal[B.ACTIVE]is not assignable toB"Expected:
error: [invalid-assignment] "Object of type
Literal[my_module.C.B.ACTIVE]is not assignable tomy_module.A.B"Solution
In this MR we added an heuristics to detect when to use a fully qualified name:
The fully qualified name always includes:
There was no
QualifiedDisplayso I had to implement it from scratch. I'm very new to the codebase, so I might have done things inefficiently, so I appreciate feedback.Should we pre-compute the fully qualified name or do it on demand?
Not implemented
Function-local classes
Should we approach this in a different PR?
Example:
mypy
t.py:8: error: Incompatible return value type (got "t.A@5", expected "t.A") [return-value]From my testing the 5 in
A@5comes from the like number.ty
Fixes astral-sh/ty#848