Skip to content

fix map is confused by string literals #2854#2856

Draft
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2854
Draft

fix map is confused by string literals #2854#2856
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2854

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #2854

starred list/set elements now infer their iterable type without a contextual hint first, and only retry with the hint when the unhinted result still contains partial placeholders.

That stops Iterable[LiteralString] from over-constraining nested calls like map(str, range(n)) while preserving empty-container inference.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Mar 23, 2026
@github-actions
Copy link

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

ibis (https://github.com/ibis-project/ibis)
+ ERROR ibis/backends/sql/compilers/snowflake.py:220:35-81: Argument `str` is not assignable to parameter `object` with type `Literal['COMMENT = \'{comment}\'', 'CREATE OR REPLACE TEMPORARY FUNCTION {name}({signature})', 'IMMUTABLE', 'LANGUAGE PYTHON', 'RETURNS {return_type}', 'RUNTIME_VERSION = \'{version}\'']` in function `list.append` [bad-argument-type]
+ ERROR ibis/backends/sql/compilers/snowflake.py:225:31-55: Argument `str` is not assignable to parameter `object` with type `Literal['COMMENT = \'{comment}\'', 'CREATE OR REPLACE TEMPORARY FUNCTION {name}({signature})', 'IMMUTABLE', 'LANGUAGE PYTHON', 'RETURNS {return_type}', 'RUNTIME_VERSION = \'{version}\'']` in function `list.append` [bad-argument-type]

openlibrary (https://github.com/internetarchive/openlibrary)
- ERROR openlibrary/plugins/worksearch/code.py:320:13-19: Argument `list[tuple[str, SolrRequestLabel] | tuple[str, int | Unknown] | tuple[str, str] | tuple[str, Unknown]]` is not assignable to parameter `cur_solr_params` with type `list[tuple[str, str]]` in function `openlibrary.plugins.worksearch.schemes.SearchScheme.q_to_solr_params` [bad-argument-type]
+ ERROR openlibrary/plugins/worksearch/code.py:320:13-19: Argument `list[tuple[Literal['fq'], str] | tuple[str, SolrRequestLabel] | tuple[str, int | Unknown] | tuple[str, Unknown]]` is not assignable to parameter `cur_solr_params` with type `list[tuple[str, str]]` in function `openlibrary.plugins.worksearch.schemes.SearchScheme.q_to_solr_params` [bad-argument-type]

strawberry (https://github.com/strawberry-graphql/strawberry)
+ ERROR strawberry/codegen/query_codegen.py:887:26-44: `list[GraphQLField]` is not assignable to variable `fields` with type `list[GraphQLField | GraphQLFragmentSpread]` [bad-assignment]

pandas (https://github.com/pandas-dev/pandas)
- ERROR pandas/tests/indexes/multi/test_indexing.py:959:36-41: Argument `range` is not assignable to parameter `iterable` with type `Iterable[_NestedSequence[_SupportsArray[dtype]] | _SupportsArray[dtype]]` in function `list.__init__` [bad-argument-type]

core (https://github.com/home-assistant/core)
+ ERROR homeassistant/components/devolo_home_control/siren.py:54:38-59:10: `list[int]` is not assignable to attribute `_attr_available_tones` with type `dict[int, str] | list[int | str] | None` [bad-assignment]
+ ERROR homeassistant/components/fritz/switch.py:203:20-89: Returned type `list[FritzBoxWifiSwitch]` is not assignable to declared return type `list[Entity]` [bad-return]
+ ERROR homeassistant/components/fritz/switch.py:206:12-211:6: Returned type `list[FritzBoxDeflectionSwitch | FritzBoxPortSwitch | FritzBoxProfileSwitch | FritzBoxWifiSwitch]` is not assignable to declared return type `list[Entity]` [bad-return]
+ ERROR homeassistant/components/home_connect/sensor.py:514:12-532:6: Returned type `list[HomeConnectEventSensor | HomeConnectProgramSensor | HomeConnectSensor]` is not assignable to declared return type `list[HomeConnectEntity]` [bad-return]
+ ERROR homeassistant/components/number/const.py:499:80-614:2: `dict[NumberDeviceClass, set[UnitOfSpeed | UnitOfVolumetricFlux] | set[str | type[StrEnum] | None]]` is not assignable to `dict[NumberDeviceClass, set[str | type[StrEnum] | None]]` [bad-assignment]
+ ERROR homeassistant/components/sensor/const.py:594:80-709:2: `dict[SensorDeviceClass, set[UnitOfSpeed | UnitOfVolumetricFlux] | set[str | type[StrEnum] | None]]` is not assignable to `dict[SensorDeviceClass, set[str | type[StrEnum] | None]]` [bad-assignment]

sphinx (https://github.com/sphinx-doc/sphinx)
- ERROR sphinx/ext/autosummary/__init__.py:607:28-43: Argument `list[str]` is not assignable to parameter `iterable` with type `Iterable[LiteralString]` in function `list.__init__` [bad-argument-type]

django-modern-rest (https://github.com/wemake-services/django-modern-rest)
+ ERROR dmr/metadata.py:365:16-370:10: Returned type `list[type[AsyncAuth] | type[Parser] | type[Renderer] | type[SyncAuth] | type[ComponentParser]]` is not assignable to declared return type `list[type[ResponseSpecProvider]]` [bad-return]

jax (https://github.com/google/jax)
+ ERROR jax/experimental/key_reuse/_core.py:389:33-66: `list[Var]` is not assignable to `list[Atom]` [bad-assignment]
- ERROR jax/experimental/mosaic/gpu/fragmented_array.py:4107:50-53: Argument `type[str]` is not assignable to parameter `func` with type `(object) -> LiteralString` in function `map.__new__` [bad-argument-type]

materialize (https://github.com/MaterializeInc/materialize)
+ ERROR misc/python/materialize/parallel_workload/action.py:2191:44-75: `list[Table | View]` is not assignable to `list[DBObject]` [bad-assignment]
+ ERROR misc/python/materialize/parallel_workload/action.py:2220:44-75: `list[Table | View]` is not assignable to `list[DBObject]` [bad-assignment]

@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 23, 2026 05:42
Copilot AI review requested due to automatic review settings March 23, 2026 05:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an inference bug where LiteralString context from str.join(...) could incorrectly over-constrain nested starred iterables (e.g., *map(str, ...)) during list/set element inference.

Changes:

  • Update list/set starred-element inference to infer without a contextual hint first, and only retry with the hint when the unhinted result contains partial (placeholder) types.
  • Add a regression test reproducing issue #2854 involving ",".join([*genexpr, *map(str, range(n))]).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pyrefly/lib/alt/expr.rs Adjusts starred element inference in list/set literals to avoid premature contextual-hint over-constraint while preserving empty-container inference via a retry.
pyrefly/lib/test/literal.rs Adds a regression testcase ensuring the join + starred + map(str, ...) pattern no longer produces overload/type errors.

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

Comment on lines +1846 to +1851
let retry_with_hint = star_hint.as_ref().is_some()
&& unpacked_ty.any(|ty| self.solver().is_partial(ty));
if retry_with_hint {
unpacked_ty = self.expr_infer_with_hint_promote(
value,
star_hint.as_ref().map(|hint| hint.as_ref()),
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

star_hint is wrapped in a LazyCell, but star_hint.as_ref().is_some() forces it to be computed even when you don't retry with the hint. Consider checking elt_hint.is_some() (or similar) to decide whether a hint exists, and only evaluating star_hint when the retry path is actually taken, so the derived Iterable[...] hint construction stays lazy.

Suggested change
let retry_with_hint = star_hint.as_ref().is_some()
&& unpacked_ty.any(|ty| self.solver().is_partial(ty));
if retry_with_hint {
unpacked_ty = self.expr_infer_with_hint_promote(
value,
star_hint.as_ref().map(|hint| hint.as_ref()),
let retry_with_hint =
elt_hint.is_some() && unpacked_ty.any(|ty| self.solver().is_partial(ty));
if retry_with_hint {
let star_hint_ref = star_hint.force();
unpacked_ty = self.expr_infer_with_hint_promote(
value,
star_hint_ref.as_ref().map(|hint| hint.as_ref()),

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

Primer Diff Classification

❌ 4 regression(s) | ✅ 3 improvement(s) | ➖ 2 neutral | 9 project(s) total | +14, -4 errors

4 regression(s) across strawberry, core, django-modern-rest, materialize. error kinds: bad-assignment, bad-return: list of subclass not assignable to list of superclass, bad-assignment: list[int] not assignable to list[int | str] | .... 3 improvement(s) across ibis, pandas, sphinx.

Project Verdict Changes Error Kinds Root Cause
ibis ✅ Improvement +2 bad-argument-type pyrefly/lib/alt/expr.rs
openlibrary ➖ Neutral +1, -1 bad-argument-type
strawberry ❌ Regression +1 bad-assignment pyrefly/lib/alt/expr.rs
pandas ✅ Improvement -1 bad-argument-type pyrefly/lib/alt/expr.rs
core ❌ Regression +6 bad-return: list of subclass not assignable to list of superclass pyrefly/lib/alt/expr.rs
sphinx ✅ Improvement -1 bad-argument-type pyrefly/lib/alt/expr.rs
django-modern-rest ❌ Regression +1 bad-return pyrefly/lib/alt/expr.rs
jax ➖ Neutral +1, -1 bad-argument-type, bad-assignment pyrefly/lib/alt/expr.rs
materialize ❌ Regression +2 bad-assignment pyrefly/lib/alt/expr.rs
Detailed analysis

❌ Regression (4)

strawberry (+1)

This is a regression. The PR changed starred element inference to avoid LiteralString over-constraining, but as a side effect, list displays with starred unpacking no longer get contextual typing when the unhinted result has no partial placeholders. The code at line 887 creates a fresh list [*sub_type.fields] assigned to a variable annotated list[GraphQLField | GraphQLFragmentSpread]. This should be valid — list displays should use contextual typing to widen element types. Both mypy and pyright accept this pattern.
Attribution: The change to starred element inference in pyrefly/lib/alt/expr.rs (the Expr::Starred branch) now infers starred elements without the contextual hint first. For [*sub_type.fields], the unhinted inference produces list[GraphQLField] (no partial placeholders), so the hint is never applied. Previously, the hint list[GraphQLField | GraphQLFragmentSpread] would have widened the result. This causes the new false positive.

core (+6)

bad-return: list of subclass not assignable to list of superclass: FritzBoxWifiSwitch is a subclass of Entity (via FritzBoxBaseSwitch -> FritzBoxBaseEntity -> Entity). However, list is invariant, so list[FritzBoxWifiSwitch] is not generally assignable to list[Entity]. For freshly constructed list literals like [*await _async_wifi_entities_list(...)], type checkers like mypy and pyright apply special-casing to allow widening to the declared return type. The PR removed the contextual hint that previously guided inference, and pyrefly now infers the narrow type and rejects it. Similarly for line 206-211 where multiple starred unpacks of different Entity subclass lists are combined. False positive.
bad-assignment: list[int] not assignable to list[int | str] | ...: The freshly constructed [*range(...)] produces list[int] without the contextual hint. Since list is invariant, list[int] is technically not a subtype of list[int | str]. However, for fresh list constructions, mypy and pyright allow widening to the target type. The PR removed the contextual type hint from the attribute declaration that would have guided inference to produce list[int | str] directly. Pyrefly now infers the narrower list[int] and rejects the assignment. False positive.

Overall: All 6 new errors are false positives caused by the PR's change to starred element inference. The PR removed contextual type hints that previously guided list literal inference to produce the expected wider types. Without those hints, pyrefly infers narrower types (e.g., list[FritzBoxWifiSwitch] instead of list[Entity], list[int] instead of list[int | str]). Since list is invariant in Python's type system, list[SubClass] is not a subtype of list[SuperClass] in general. However, for freshly constructed list literals that haven't escaped, type checkers like mypy and pyright apply special-casing to allow widening to the target type. Pyrefly lacks this special-casing (or the PR broke it by removing the contextual hint), causing it to reject these valid patterns. Both mypy and pyright accept all of these patterns (0/6 cross-check).

Attribution: The change to starred element inference in pyrefly/lib/alt/expr.rs (the Expr::Starred match arm) now infers starred expressions without the contextual hint first. When the result is a concrete (non-partial) type like list[int] or list[FritzBoxWifiSwitch], the hint is never applied, producing a more specific type. Pyrefly then fails to recognize this specific type as assignable to the broader expected type (e.g., list[int] to list[int | str], or list[FritzBoxWifiSwitch] to list[Entity]).

django-modern-rest (+1)

This is a case where the PR's change to starred element inference causes a loss of contextual type information. Previously, the contextual hint from the return type (list[type[ResponseSpecProvider]]) was propagated into the starred expressions, allowing the list elements to be typed as type[ResponseSpecProvider]. After the PR, the starred elements are first inferred without the hint, producing concrete types like type[Parser], type[Renderer], etc. Since list is invariant, list[type[Parser] | type[Renderer] | ...] is not assignable to list[type[ResponseSpecProvider]].

Looking at the code, the types come from the field declarations on EndpointMetadata:

  • self.component_parsers is list[ComponentParserSpec] where ComponentParserSpec = tuple[type['ComponentParser'], tuple[Any, ...]], so spec[0] yields type[ComponentParser]
  • self.parsers is dict[str, 'Parser'], so type(parser) yields type[Parser]
  • self.renderers is dict[str, 'Renderer'], so type(renderer) yields type[Renderer]
  • self.auth is list['SyncAuth | AsyncAuth'] | None, so type(auth) yields type[SyncAuth] | type[AsyncAuth]

Whether these classes actually inherit from ResponseSpecProvider is not visible in this file - they are imported from other modules. Given the project structure and that collect_response_specs calls provider.provide_response_specs(...) on the results, it's very likely these classes DO inherit from ResponseSpecProvider.

Even if they are subclasses, the key issue is list invariance: list[type[SubClass]] is not assignable to list[type[BaseClass]]. Note that each individual element WOULD be fine - type[Parser] is assignable to type[ResponseSpecProvider] if Parser is a subclass of ResponseSpecProvider, since type is covariant for subclass relationships. The problem is specifically that the list literal's type is inferred as list[type[AsyncAuth] | type[Parser] | type[Renderer] | type[SyncAuth] | type[ComponentParser]] rather than list[type[ResponseSpecProvider]].

Previously, the contextual return type hint would guide the list literal to be typed as list[type[ResponseSpecProvider]], and each element would be checked for assignability to type[ResponseSpecProvider] individually (which would succeed for subclasses). After the PR's change to starred element inference, the contextual hint is lost for starred expressions, causing the type checker to infer the most specific union type for the list elements, which then fails the invariance check against the declared return type.

This is a regression introduced by the PR's change to starred element inference. The fix for the map(str, range(n)) issue has the side effect of losing contextual type hints for starred expressions, leading to overly-specific type inference that then fails invariance checks.

Attribution: The change in pyrefly/lib/alt/expr.rs modified how starred list elements are inferred. Previously, starred elements always used the contextual hint (the return type list[type[ResponseSpecProvider]]), which would have constrained the element types to type[ResponseSpecProvider]. Now, starred elements first infer without the hint, and only retry with the hint if the unhinted result contains partial placeholders. Since type(parser) etc. resolve to concrete types without needing a hint, the retry doesn't happen, and the list gets inferred as the union of concrete types rather than being constrained to type[ResponseSpecProvider]. This causes the bad-return error because list is invariant.

materialize (+2)

While list is technically invariant per the typing spec (https://typing.readthedocs.io/en/latest/spec/generics.html#variance), both mypy and pyright accept this pattern — constructing a fresh list literal [*a, *b] and assigning it to a wider type annotation. The PR's change to starred element inference now causes pyrefly to infer list[Table | View] instead of list[DBObject], triggering the invariance check. Since neither mypy nor pyright flag this (it's a common, safe pattern where the list is freshly constructed), and the code is correct in practice, these are false positives introduced by the PR's inference change.
Attribution: The change in pyrefly/lib/alt/expr.rs modified how starred elements (*expr) in list/set literals are inferred. Previously, starred elements were inferred with a contextual hint (the target type). Now, they are first inferred WITHOUT a hint, and only retried with the hint if the unhinted result contains partial placeholders. This means [*exe.db.tables, *exe.db.views] now infers the starred elements as their concrete types (list[Table] and list[View]) without being guided by the list[DBObject] hint, producing list[Table | View] instead of list[DBObject]. The old behavior used the hint to widen the element types to DBObject, making the assignment succeed.

✅ Improvement (3)

ibis (+2)

This is a regression caused by the PR's change to starred element inference. On line 217, preamble_lines = [*self._UDF_PREAMBLE_LINES] unpacks a tuple of 6 literal strings. Previously, the contextual hint would widen the element type to str. Now, without the hint, pyrefly infers the list as list[Literal['CREATE OR REPLACE TEMPORARY FUNCTION {name}({signature})', ...]] — a union of 6 specific literal strings. Since these literals are fully resolved (no partial placeholders), the retry-with-hint path is skipped. When .append(f"IMPORTS = ...") (line 220) and .append(f"PACKAGES = ...") (line 225) are called, the f-string has type str, which is not assignable to the narrow Literal[...] union. This is a false positive — str values should be appendable to a list[str], and the list should have been inferred as list[str] since LiteralString types should widen to str in this context. Neither mypy nor pyright flag this.
Attribution: The change in pyrefly/lib/alt/expr.rs modifies how starred elements in list literals are inferred. Previously, starred elements always used the contextual hint for inference. Now, they first infer without a hint, and only retry with the hint if the result contains partial placeholders. In this case, [*self._UDF_PREAMBLE_LINES] is a starred element in the list literal on line 217. The tuple _UDF_PREAMBLE_LINES has type tuple[Literal['CREATE OR REPLACE...'], Literal['RETURNS...'], ...] — a tuple of 6 specific literal strings. Without the contextual hint, pyrefly infers the list element type as the union of those 6 literals rather than widening to str. Since the unhinted result (Literal[...]) has no partial placeholders, the retry-with-hint path is never taken. This causes the list to be typed as list[Literal['CREATE OR REPLACE...', 'RETURNS...', ...]] instead of list[str], making subsequent .append(f"...") calls fail because str is not assignable to that narrow Literal union.

pandas (-1)

This is a clear improvement. The removed error was a false positive where pyrefly incorrectly flagged list(range(...)) as having an incompatible argument type. The range type is obviously a valid Iterable[int] for list.__init__. The error was caused by the contextual hint from np.array() propagating too aggressively into the starred expression, constraining list()'s parameter to numpy's _NestedSequence[_SupportsArray[dtype]] | _SupportsArray[dtype] type. The PR fix correctly infers starred elements without the hint first, preventing this over-constraining behavior.
Attribution: The change in pyrefly/lib/alt/expr.rs in the starred element handling within elts.map() is directly responsible. Previously, starred elements always used expr_infer_with_hint_promote with the contextual hint from the outer container (in this case, numpy's array type). The new code first calls self.expr_infer(value, errors) without the hint, then only retries with the hint if the result contains partial types (is_partial). This prevents Iterable[LiteralString] or numpy's complex nested sequence types from over-constraining nested calls like list(range(...)) inside starred expressions.

sphinx (-1)

This was a false positive caused by LiteralString contextual hints leaking through starred unpacking. The PR correctly fixes this by inferring starred elements without hints first, only applying hints when needed for partial types. Removing this false positive is an improvement.
Attribution: The change to the starred element inference logic in pyrefly/lib/alt/expr.rs (around line 1845) now infers starred elements without the contextual hint first, and only retries with the hint when the unhinted result contains partial placeholders. This prevents the LiteralString hint from str.join from over-constraining list(items[:n_items]) which naturally resolves to list[str].

➖ Neutral (2)

openlibrary (+1, -1)

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

jax (+1, -1)

The PR correctly fixes the map(str, range(n)) false positive by not over-constraining starred elements with contextual hints. However, it introduces a new false positive where unpacking list[Var] into a list literal annotated as list[Atom] fails because the unhinted inference produces list[Var] (which has no partial placeholders, so the hint is never retried), and then invariance prevents assignment to list[Atom]. Net effect: one false positive removed, one false positive added — roughly neutral but the new error is a regression from the PR's inference change.
Attribution: The change to expr.rs in pyrefly/lib/alt/expr.rs modified how starred list/set elements infer their iterable type. Previously, starred elements always used the contextual hint for inference. Now they first infer without a hint and only retry with the hint if the result contains partial placeholders. This fixed the map(str, range(n)) false positive (removed error) but introduced a regression where [*jaxpr.invars, *jaxpr.constvars] now infers as list[Var] instead of using the list[Atom] annotation hint, causing the new bad-assignment error.

Suggested fixes

Summary: The PR's change to starred element inference fixes LiteralString over-constraining but breaks contextual typing for starred unpacking in list literals, causing 10+ false positives across 6 projects where list[SubClass] is not assignable to list[SuperClass] due to lost type widening.

**1. In the starred element inference logic in pyrefly/lib/alt/expr.rs (the Expr::Starred match arm around line 1845), change the retry condition to also retry with the hint when the unhinted result type is NOT assignable to the hint type (not just when it contains partial placeholders). Specifically, after inferring unpacked_ty without a hint, check: (1) if the result has partial placeholders (existing check), OR (2) if the hint exists and the unhinted result is a concrete iterable type whose element type is a strict subtype of the hint's element type (i.e., the unhinted type would fail an invariance check against the target). In pseudo-code:

rust
let mut unpacked_ty = self.expr_infer(value, errors);
let retry_with_hint = star_hint.as_ref().is_some()
&& (unpacked_ty.any(|ty| self.solver().is_partial(ty))
|| !self.is_assignable(&unpacked_ty, star_hint.as_ref().unwrap()));
if retry_with_hint {
unpacked_ty = self.expr_infer_with_hint_promote(
value,
star_hint.as_ref().map(|hint| hint.as_ref()),
errors,
);
}

This preserves the fix for the map(str, range(n)) case (where the unhinted list[int] IS assignable to the hint, so no retry happens and the LiteralString over-constraining is avoided), while restoring contextual typing for cases where the unhinted type would fail assignability (e.g., list[FritzBoxWifiSwitch] not assignable to list[Entity] due to invariance, so retry with hint produces list[Entity] directly).**

Files: pyrefly/lib/alt/expr.rs
Confidence: medium
Affected projects: strawberry, core, django-modern-rest, materialize, ibis, jax
Fixes: bad-assignment, bad-return
The core issue is that the PR's is_partial check is too narrow a condition for retrying with the hint. The original fix correctly addresses the case where map(str, range(n)) gets over-constrained by a LiteralString hint — in that case, the unhinted result (list[int]) is perfectly valid and assignable. But for cases like [*wifi_entities] where the unhinted result is list[SubClass] and the target is list[SuperClass], the unhinted result is NOT assignable due to list invariance, so the hint SHOULD be applied to widen the type. By checking assignability as a retry condition, we get the best of both worlds: no over-constraining when the unhinted type works, but proper widening when it doesn't. This would eliminate ~10 false positives across strawberry (1), core (6), django-modern-rest (1), materialize (2), plus the regressions in ibis (2) and jax (1) that were classified as improvements/neutral but are actually also caused by this change.

**2. Alternative simpler fix: In the starred element inference logic in pyrefly/lib/alt/expr.rs, instead of checking only is_partial, also check if the unhinted result contains LiteralString or Literal[...] string types. Only skip the hint when the unhinted result would be over-constrained by a LiteralString hint specifically. In pseudo-code:

rust
let mut unpacked_ty = self.expr_infer(value, errors);
let hint_would_overconstrain = star_hint.as_ref().map_or(false, |hint| {
hint.any(|ty| self.solver().is_literal_string(ty))
});
let retry_with_hint = star_hint.as_ref().is_some()
&& !hint_would_overconstrain
|| unpacked_ty.any(|ty| self.solver().is_partial(ty));
if retry_with_hint {
unpacked_ty = self.expr_infer_with_hint_promote(
value,
star_hint.as_ref().map(|hint| hint.as_ref()),
errors,
);
}

This narrowly targets the original problem (LiteralString hints over-constraining starred elements) while preserving contextual typing for all other cases.**

Files: pyrefly/lib/alt/expr.rs
Confidence: medium
Affected projects: strawberry, core, django-modern-rest, materialize, ibis, jax
Fixes: bad-assignment, bad-return
The original bug was specifically about LiteralString hints leaking through starred unpacking (the pandas and sphinx fixes). By only suppressing the hint when it contains LiteralString, we fix the original issue without breaking contextual typing for the common pattern of starred unpacking into a wider list type. This is a more targeted fix but may miss edge cases where other hint types also over-constrain. Would eliminate the same ~10 false positives across all affected projects.


Was this helpful? React with 👍 or 👎

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

@asukaminato0721 asukaminato0721 marked this pull request as draft March 23, 2026 06:56
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.

map is confused by string literals

2 participants