Skip to content

fix Narrow types based on collection containment #2706#2710

Open
asukaminato0721 wants to merge 3 commits into
facebook:mainfrom
asukaminato0721:2706
Open

fix Narrow types based on collection containment #2706#2710
asukaminato0721 wants to merge 3 commits into
facebook:mainfrom
asukaminato0721:2706

Conversation

@asukaminato0721

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2706

containment narrowing now derives finite membership domains directly from typed RHS containers instead of relying only on the generic iterable/mapping decomposition.

That preserves literal precision for typed dict, tuple, list, set, and frozenset values, and the literal fast path now also recognizes inline frozenset(...) calls

Test Plan

add the test in issue

@meta-cla meta-cla Bot added the cla signed label Mar 8, 2026
@asukaminato0721
asukaminato0721 marked this pull request as ready for review March 8, 2026 11:14
Copilot AI review requested due to automatic review settings March 8, 2026 11:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves containment-based type narrowing so that x in <typed collection> can narrow x to a finite domain derived from the RHS container’s typed element/key domain (preserving literal precision for typed dict, tuple, list, set, and frozenset, including frozenset(...) calls).

Changes:

  • Add helper routines to derive a finite “membership domain” type from RHS container types (including TypedDict keys and typed container element types).
  • Extend the syntactic fast-path for membership narrowing to recognize frozenset(<literal container>).
  • Add a regression test covering narrowing for in checks against typed collection variables and inline frozenset(...).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
pyrefly/lib/alt/narrow.rs Implements finite-domain membership narrowing from typed RHS containers and recognizes inline frozenset(...) for the literal fast-path.
pyrefly/lib/test/narrow.rs Adds a test ensuring in-based narrowing works for typed dict/tuple/list/set/frozenset and frozenset(("a",)).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@yangdanny97

Copy link
Copy Markdown
Contributor

it's weird that mypy primer timed out, i wonder if there is a perf regression somewhere

@rchen152

rchen152 commented Mar 8, 2026

Copy link
Copy Markdown
Contributor

it's weird that mypy primer timed out, i wonder if there is a perf regression somewhere

@yangdanny97 I've also started seeing mypy_primer timeouts in the past day or so (but only with the GitHub workflow).

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@yangdanny97

Copy link
Copy Markdown
Contributor

@migeed-z the error count diff does not match the analysis for pydantic

@yangdanny97 yangdanny97 self-assigned this Mar 11, 2026
@meta-codesync

meta-codesync Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D96155516.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@stroxler stroxler left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some tests for upcasting / type erasure, and improve the behavior if we see a problem

Comment thread pyrefly/lib/alt/narrow.rs
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added size/l and removed size/l labels Mar 28, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

containment narrowing now derives finite membership domains directly
from typed RHS containers instead of relying only on the generic
iterable/mapping decomposition. That preserves literal precision for
typed dict, tuple, list, set, and frozenset values, and the literal fast
path now also recognizes inline frozenset(...) calls
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Diff from mypy_primer, showing the effect of this PR on open source code:

pip (https://github.com/pypa/pip)
- ERROR src/pip/_vendor/urllib3/util/ssl_.py:280:66-282:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MINIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/pip/_vendor/urllib3/util/ssl_.py:283:66-285:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MAXIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/pip/_vendor/urllib3/util/ssl_.py:301:35-54: `int | Unknown` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/pip/_vendor/urllib3/util/ssl_.py:301:35-54: `int` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
- ERROR src/pip/_vendor/urllib3/util/ssl_.py:306:35-54: `int | Unknown` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/pip/_vendor/urllib3/util/ssl_.py:306:35-54: `int` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/_internal/launchers.py:64:13-29: Cannot set item in `BundleLauncherOverride` [unsupported-operation]

urllib3 (https://github.com/urllib3/urllib3)
- ERROR src/urllib3/util/ssl_.py:235:66-237:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MINIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/urllib3/util/ssl_.py:238:66-240:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MAXIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/urllib3/util/ssl_.py:254:35-54: `int | Unknown` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/urllib3/util/ssl_.py:254:35-54: `int` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
- ERROR src/urllib3/util/ssl_.py:259:35-54: `int | Unknown` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/urllib3/util/ssl_.py:259:35-54: `int` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]

comtypes (https://github.com/enthought/comtypes)
+ ERROR comtypes/tools/codegenerator/codegenerator.py:96:30-34: Argument `Any | None` is not assignable to parameter `symbolname` with type `str` in function `comtypes.tools.codegenerator.namespaces.ImportedNamespaces.add` [bad-argument-type]

Tanjun (https://github.com/FasterSpeeding/Tanjun)
- ERROR tanjun/context/menu.py:144:16-28: Returned type `CommandType` is not assignable to declared return type `Literal[CommandType.MESSAGE, CommandType.USER]` [bad-return]

static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/interface.py:1443:28-36: `CallGuard | ContainerBase | ContainerOperand | ContainerOperandSequence | DisplayActive | DisplayConfig | IndexBase | Platform | Require | StoreConfigBase | StoreFilter | WWW` is not assignable to variable `instance` with type `ContainerBase` [bad-assignment]

mypy (https://github.com/python/mypy)
- ERROR mypyc/irbuild/util.py:142:13-23: Cannot set item in `MypycAttrs` [unsupported-operation]
- ERROR mypyc/irbuild/util.py:143:19-22: Cannot set item in `dict[MypycAttr, int]` [unsupported-operation]

core (https://github.com/home-assistant/core)
+ ERROR homeassistant/components/modbus/entity.py:308:17-82: Cannot index into `dict[str, tuple[str, str] | tuple[str, None]]` [bad-index]
+ ERROR homeassistant/components/modbus/entity.py:365:35-55: Argument `Unknown | None` is not assignable to parameter `address` with type `int` in function `homeassistant.components.modbus.modbus.ModbusHub.async_pb_call` [bad-argument-type]

pydantic (https://github.com/pydantic/pydantic)
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `arguments_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_factory` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_factory_takes_data` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_host` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_path` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_port` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `definitions` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `discriminator` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `expected` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extra_behavior` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extras_keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extras_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `fail_fast` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `from_attributes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `frozen` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `function_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `ge` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `generic_origin` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `gt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `host_required` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `items_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `json_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `json_schema_input_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `lax_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `le` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `lt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `max_digits` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `max_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `members` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `microseconds_precision` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `min_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `missing` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `model_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `multiple_of` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `now_op` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `now_utc_offset` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `on_error` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `pattern` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `post_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `python_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `regex_engine` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `return_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `revalidate_instances` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `root_model` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `schema_ref` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `slots` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `steps` [bad-typed-dict-key]
- ERROR pydantic/json_schema.py:2372:60-68: Invalid key for TypedDict `AfterValidatorFunctionSchema`, got `str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strict` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strict_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strip_whitespace` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `sub_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `to_lower` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `to_upper` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `total` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `tz_constraint` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_by_alias` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_by_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `values_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_args_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_kwargs_mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_kwargs_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `variadic_item_index` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `version` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `arguments_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default_factory` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default_factory_takes_data` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default_host` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default_path` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `default_port` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `definitions` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `discriminator` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `expected` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `extra_behavior` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `extras_keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `extras_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `fail_fast` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `from_attributes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `frozen` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `function` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `function_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `ge` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `generic_origin` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `gt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `host_required` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `items_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `json_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `json_schema_input_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `lax_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `le` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `lt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `max_digits` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `max_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `members` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `microseconds_precision` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `min_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `missing` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `model_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `multiple_of` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `now_op` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `now_utc_offset` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `on_error` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `pattern` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `post_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `python_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `regex_engine` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `return_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `revalidate_instances` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `root_model` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `schema_ref` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `slots` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `steps` [bad-typed-dict-key]
- ERROR pydantic/json_schema.py:2372:60-68: Invalid key for TypedDict `AnySchema`, got `str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `strict` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `strict_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `strip_whitespace` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `sub_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `to_lower` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `to_upper` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `total` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `tz_constraint` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `validate_by_alias` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `validate_by_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `validate_default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `values_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `var_args_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `var_kwargs_mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `var_kwargs_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `variadic_item_index` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `AnySchema` does not have key `version` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2372:60-68: TypedDict `ArgumentsSchema` does not have key `default_factory` [bad-typed-dict-key]

... (truncated 4159 lines) ...

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Primer Diff Classification

❌ 1 regression(s) | ✅ 7 improvement(s) | ➖ 1 neutral | 9 project(s) total | +9, -13 errors

1 regression(s) across core. error kinds: bad-index false positive from containment narrowing, bad-argument-type false positive from containment narrowing. caused by membership_narrow_container_type(). 7 improvement(s) across pip, prefect, urllib3, comtypes, Tanjun, static-frame, mypy.

Project Verdict Changes Error Kinds Root Cause
pip ✅ Improvement +2, -4 bad-assignment, no-matching-overload membership_narrow_container_type()
prefect ✅ Improvement -1 unsupported-operation pyrefly/lib/alt/narrow.rs
urllib3 ✅ Improvement +2, -4 bad-assignment, no-matching-overload membership_narrow_container_type()
comtypes ✅ Improvement +1 bad-argument-type membership_narrow_container_type()
Tanjun ✅ Improvement -1 bad-return membership_narrow_container_type()
static-frame ✅ Improvement +1 bad-assignment membership_narrow_container_type()
mypy ✅ Improvement -2 unsupported-operation membership_narrow_container_type()
core ❌ Regression +2 bad-index false positive from containment narrowing membership_narrow_container_type()
pydantic ➖ Neutral +1, -1 bad-typed-dict-key
Detailed analysis

❌ Regression (1)

core (+2)

bad-index false positive from containment narrowing: Line 308: The convert dict is indexed with the result of config[CONF_VERIFY].get(CONF_INPUT_TYPE, config[CONF_WRITE_TYPE]). The new containment narrowing logic (triggered by CONF_VERIFY in config on line 299) appears to narrow config's type in a way that makes the .get() return type incompatible with convert's key type. This is a false positive — the code is correct at runtime.
bad-argument-type false positive from containment narrowing: Line 365: self._verify_address is assigned on line 304 inside the if CONF_VERIFY in config: block. The new narrowing logic appears to introduce None into the type of self._verify_address (making it Unknown | None), which then fails the int parameter check for async_pb_call. This is a cascade from the same narrowing change. Neither mypy nor pyright flags this.

Overall: These are false positives introduced by the PR's containment narrowing changes. The convert dict on line 276 has type dict[str, tuple[str, str] | tuple[str, None]] — its values include tuples where the second element can be None. The in check CONF_VERIFY in config on line 299 triggers the new narrowing logic, which appears to incorrectly affect the typing of config[CONF_VERIFY] and downstream expressions. Error 1: The expression config[CONF_VERIFY].get(CONF_INPUT_TYPE, config[CONF_WRITE_TYPE]) returns a string at runtime (it's getting a value from a dict with a string default), and indexing convert with it is valid. The bad-index error suggests pyrefly is now inferring a type for this expression that doesn't match the dict's key type. Error 2: self._verify_address is set inside the if CONF_VERIFY in config: block and used later in _async_update. The narrowing change appears to have introduced None into the type of self._verify_address, making it Unknown | None instead of just the Unknown it was before. Neither mypy nor pyright flags these, confirming they are false positives from overly aggressive narrowing.

Attribution: The changes to membership_narrow_container_type() and membership_narrow_type() in pyrefly/lib/alt/narrow.rs introduced new containment-based narrowing logic. Specifically, the new membership_narrow_container_type method handles dict types by narrowing to the key type. When CONF_VERIFY in config is checked (line 299), the new narrowing logic likely narrows config from dict to something more specific, which then affects how config[CONF_VERIFY] is typed. This cascades: (1) config[CONF_VERIFY].get(...) now returns a narrowed type that doesn't match the convert dict's key type (error at line 308), and (2) self._verify_address gets typed as Unknown | None instead of Unknown (error at line 365).

✅ Improvement (7)

pip (+2, -4)

Net improvement: 4 errors removed (2 false positive no-matching-overload + 2 imprecise bad-assignment with Unknown), replaced by 2 correct bad-assignment errors that pyright also flags. The new errors correctly identify that int is not assignable to TLSVersion — the function parameters are typed as int | None but SSLContext.minimum_version/maximum_version expect TLSVersion. This is a real type issue in the code (which the code works around at runtime since SSLContext accepts int values internally).
Attribution: The change to membership_narrow_container_type() in pyrefly/lib/alt/narrow.rs improved containment narrowing for typed containers. This allowed pyrefly to better narrow ssl_version through the not in (None, PROTOCOL_TLS, PROTOCOL_TLS_CLIENT) check on line 268, which in turn fixed the dict.get overload resolution. With the overload now resolving correctly, the return type of dict.get became int (precise) instead of int | Unknown, and the downstream assignment errors became more precise (int not assignable to TLSVersion instead of int | Unknown not assignable).

prefect (-1)

This is a clear improvement. The removed error was a false positive caused by insufficient type narrowing. The PR improves containment-based narrowing to derive literal types from typed containers (tuples, TypedDicts, lists, sets, frozensets), which allows pyrefly to correctly determine that side is Literal['upload'] | Literal['execution'] rather than just str, making the TypedDict item assignment valid.
Attribution: The new membership_narrow_container_type and membership_narrow_tuple_type methods in pyrefly/lib/alt/narrow.rs enable narrowing based on typed container contents. Specifically, when side in launcher is checked, the TypedDict handling in membership_narrow_container_type (or the tuple handling for the iteration variable) allows pyrefly to derive that side has a literal type compatible with the TypedDict keys, removing the false unsupported-operation error.

urllib3 (+2, -4)

Net improvement: 4 false positives removed, 2 true positives added. The removed errors were caused by pyrefly's inability to narrow through not in containment checks. The new errors correctly identify that int is not assignable to TLSVersion — confirmed by pyright. The function parameters are typed as int | None but the SSLContext attributes expect TLSVersion, which is a genuine type mismatch in urllib3's code.
Attribution: The changes to membership_narrow_container_type() and the refactored narrowing logic in pyrefly/lib/alt/narrow.rs improved containment-based narrowing. This allowed ssl_version not in (None, PROTOCOL_TLS, PROTOCOL_TLS_CLIENT) to properly narrow ssl_version from int | None to int, fixing the false no-matching-overload on dict.get. With the overload resolving correctly, dict.get now returns int (the value type of dict[int, int]), which then correctly triggers the bad-assignment when assigned to TLSVersion-typed attributes.

comtypes (+1)

This is a legitimate type issue. name has type Any | None as reported by pyrefly. The generate method's item parameter is untyped, and getattr(item, 'name', None) returns Any | None because the default value None is included in the return type. The in check (name in self.known_symbols) does not narrow away None. Passing Any | None to a parameter expecting str is a valid error. Pyright also flags this (as noted in the error annotation pyright: yes). The code should either add a name is not None guard or type-narrow differently. The PR's narrowing changes likely stopped an overly-aggressive narrowing that was masking this issue.
Attribution: The changes to membership_narrow_container_type() and the refactored containment narrowing logic in pyrefly/lib/alt/narrow.rs changed how in checks narrow types. The old code may have been more aggressively narrowing name after the in check (or not reaching this error path), while the new code correctly does not narrow Any | None to str just because of an in check on a dict with unknown key types. This exposed the pre-existing type mismatch.

Tanjun (-1)

This is a clear improvement. The PR adds containment-based narrowing for typed collections like frozenset[Literal[...]]. In the tanjun code, _VALID_TYPES is declared as frozenset[typing.Literal[hikari.CommandType.USER, hikari.CommandType.MESSAGE]], and after assert command_type in _VALID_TYPES, command_type should be narrowed to Literal[CommandType.USER, CommandType.MESSAGE]. Previously pyrefly couldn't perform this narrowing and incorrectly reported a bad-return error. Now it correctly narrows through the containment check and the false positive is eliminated.
Attribution: The membership_narrow_container_type() method added in pyrefly/lib/alt/narrow.rs now handles ClassType containers including frozenset. Specifically, the match arm ("list" | "set" | "frozenset", [elt]) => self.membership_narrow_type(elt) allows pyrefly to extract the element type from a frozenset[Literal[...]] and use it for narrowing. This directly enables narrowing command_type from CommandType to Literal[CommandType.USER, CommandType.MESSAGE] after the assert command_type in _VALID_TYPES check.

static-frame (+1)

This is a genuine type inconsistency. The variable instance is annotated as ContainerBase on line 1415, but _CLS_INIT_SIMPLE contains classes like CallGuard, DisplayConfig, StoreFilter, Platform, WWW, and Require that are NOT subclasses of ContainerBase. When target in cls._CLS_INIT_SIMPLE is true and instance = target() is executed, the result may not be a ContainerBase. Pyright also flags this. The new narrowing correctly identifies the type mismatch.
Attribution: The new membership_narrow_container_type() method in pyrefly/lib/alt/narrow.rs now recognizes frozenset element types. When target in cls._CLS_INIT_SIMPLE is checked (where _CLS_INIT_SIMPLE is a frozenset of class objects), pyrefly narrows target to the union of those classes. Since many of those classes (CallGuard, Require, WWW, DisplayConfig, etc.) are not subclasses of ContainerBase, the result of target() doesn't match the declared type of instance: ContainerBase.

mypy (-2)

The PR improves containment-based type narrowing for typed collections. Before, pyrefly couldn't narrow key: str to MypycAttr after key in MYPYC_ATTRS (where MYPYC_ATTRS: frozenset[MypycAttr]), causing false unsupported-operation errors on valid TypedDict and dict indexing. The new membership_narrow_container_type() correctly derives the element type from frozenset, enabling proper narrowing. Both removed errors were false positives.
Attribution: The new membership_narrow_container_type() method in pyrefly/lib/alt/narrow.rs recognizes frozenset[MypycAttr] as a typed container and extracts the element type MypycAttr for narrowing. The ("list" | "set" | "frozenset", [elt]) => self.membership_narrow_type(elt) match arm specifically handles this case, allowing key to be narrowed from str to MypycAttr when key in MYPYC_ATTRS is true.

➖ Neutral (1)

pydantic (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

Suggested fixes

Summary: The new membership_narrow_container_type() for dict types, combined with the new unwrap_iterable fallback, causes false positives in the core project by over-narrowing types after in checks on dict containers.

1. In the In narrowing branch in pyrefly/lib/alt/narrow.rs (around line 1190-1200 of the new code), the membership_narrow_container_type call for dict types duplicates the unwrap_mapping path but goes through membership_narrow_type which is meant for finite/literal types only. For dict types, membership_narrow_container_type should NOT handle them — dict key narrowing should only happen through the existing unwrap_mapping path. Remove the ("dict", [key, _]) => self.membership_narrow_type(key) arm from membership_narrow_container_type(), so that dict containers are not handled by the container narrowing path and instead fall through to the unwrap_mapping code path which has proper guards. This prevents double-narrowing where both membership_narrow_container_type and unwrap_mapping could apply to the same dict type, and avoids the cascading type errors.

Files: pyrefly/lib/alt/narrow.rs
Confidence: medium
Affected projects: core
Fixes: bad-index, bad-argument-type
The dict arm in membership_narrow_container_type overlaps with the unwrap_mapping fallback. For dict types with non-literal keys (like dict[str, ...]), membership_narrow_type returns None so it's harmless, but for dicts with literal or union keys it could produce an overly narrow intersection. Removing the dict arm from membership_narrow_container_type ensures dict narrowing only goes through the well-tested unwrap_mapping path. This should eliminate the 2 pyrefly-only false positives in core.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (1 heuristic, 8 LLM)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Narrow types based on collection containment

5 participants