mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-08 20:58:05 +00:00
![]() ## Summary We currently infer a `@Todo` type whenever we access an attribute on an intersection type with negative components. This can happen very naturally. Consequently, this `@Todo` type is rather pervasive and hides a lot of true positives that ty could otherwise detect: ```py class Foo: attr: int = 1 def _(f: Foo | None): if f: reveal_type(f) # Foo & ~AlwaysFalsy reveal_type(f.attr) # now: int, previously: @Todo ``` The changeset here proposes to handle member access on these intersection types by simply ignoring all negative contributions. This is not always ideal: a negative contribution like `~<Protocol with members 'attr'>` could be a hint that `.attr` should not be accessible on the full intersection type. The behavior can certainly be improved in the future, but this seems like a reasonable initial step to get rid of this unnecessary `@Todo` type. ## Ecosystem analysis There are quite a few changes here. I spot-checked them and found one bug where attribute access on pure negation types (`~P == object & ~P`) would not allow attributes on `object` to be accessed. After that was fixed, I only see true positives and known problems. The fact that a lot of `unused-ignore-comment` diagnostics go away are also evidence for the fact that this touches a sensitive area, where static analysis clashes with dynamically adding attributes to objects: ```py … # type: ignore # Runtime attribute access ``` ## Test Plan Updated tests. |
||
---|---|---|
.. | ||
annotations | ||
assignment | ||
binary | ||
boolean | ||
boundness_declaredness | ||
call | ||
class | ||
comparison | ||
comprehensions | ||
conditional | ||
dataclasses | ||
declaration | ||
diagnostics | ||
directives | ||
doc | ||
exception | ||
expression | ||
function | ||
generics | ||
ide_support | ||
import | ||
literal | ||
loops | ||
narrow | ||
regression | ||
scopes | ||
shadowing | ||
snapshots | ||
stubs | ||
subscript | ||
suppressions | ||
type_compendium | ||
type_of | ||
type_properties | ||
type_qualifiers | ||
unary | ||
with | ||
.mdformat.toml | ||
attributes.md | ||
cycle.md | ||
decorators.md | ||
del.md | ||
deprecated.md | ||
descriptor_protocol.md | ||
enums.md | ||
exhaustiveness_checking.md | ||
final.md | ||
instance_layout_conflict.md | ||
intersection_types.md | ||
invalid_syntax.md | ||
known_constants.md | ||
mdtest_config.md | ||
mdtest_custom_typeshed.md | ||
metaclass.md | ||
mro.md | ||
named_tuple.md | ||
overloads.md | ||
pep695_type_aliases.md | ||
properties.md | ||
protocols.md | ||
public_types.md | ||
statically_known_branches.md | ||
sys_platform.md | ||
sys_version_info.md | ||
terminal_statements.md | ||
ty_extensions.md | ||
typed_dict.md | ||
union_types.md | ||
unpacking.md | ||
unreachable.md |