Skip to content

fix autocomplete for discriminated unions #1442#3023

Open
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:1442
Open

fix autocomplete for discriminated unions #1442#3023
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:1442

Conversation

@asukaminato0721
Copy link
Copy Markdown
Contributor

Summary

Fixes #1442

The completion engine now recovers the enclosing expected type for incomplete dict literals, narrows union members from already-entered literal fields like "kind": "foo", and uses that narrowed set for both key suggestions and literal value suggestions.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Apr 5, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 5, 2026 15:30
Copilot AI review requested due to automatic review settings April 5, 2026 15:30
Copy link
Copy Markdown

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

This PR fixes LSP completions for discriminated unions of TypedDict in dict literals by recovering the dict literal’s expected/contextual type, narrowing union members based on already-entered literal fields (e.g. "kind": "foo"), and using the narrowed set to drive both key and literal-value suggestions.

Changes:

  • Add TypedDict-union narrowing for dict literals based on already-present literal fields.
  • Add dict-literal string value completion for Literal[...]-typed TypedDict fields (e.g. completing "kind": "|") in dict literals.
  • Add regression tests covering both discriminated-union value completion and narrowed key completion.

Reviewed changes

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

File Description
pyrefly/lib/state/lsp/dict_completions.rs Implements expected-type recovery for dict literals, TypedDict-union member narrowing, filters already-present keys, and adds dict value literal completions.
pyrefly/lib/lsp/wasm/completion.rs Wires the new dict value literal completion provider into the completion pipeline.
pyrefly/lib/test/lsp/completion.rs Adds regression tests for discriminated-union TypedDict key/value completion behavior (issue #1442).

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

Comment on lines +159 to +175
fn typed_dict_field_type_in_member<'b>(
solver: &crate::alt::answers_solver::AnswersSolver<
crate::state::lsp::TransactionHandle<'b>,
>,
member: &Type,
key: &str,
) -> Option<Type> {
let typed_dict = match member {
Type::TypedDict(td) | Type::PartialTypedDict(td) => td,
_ => return None,
};
solver
.type_order()
.typed_dict_fields(typed_dict)
.iter()
.find_map(|(name, field)| (name == key).then(|| field.ty.clone()))
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

typed_dict_field_type_in_member linearly scans typed_dict_fields(...) for every (member, item) pair during narrowing. Since completions run on essentially every keystroke, this can become noticeably expensive for large TypedDicts or large unions. Consider building a per-member field-name -> type map once (or otherwise avoiding repeated linear scans) inside the narrowing / key-collection paths.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +263
fn dict_literal_present_keys(
dict: &ExprDict,
skip_key_range: Option<TextRange>,
) -> BTreeMap<String, ()> {
dict.items
.iter()
.filter_map(|item| {
let Expr::StringLiteral(lit) = item.key.as_ref()? else {
return None;
};
(skip_key_range != Some(lit.range())).then(|| (lit.value.to_string(), ()))
})
.collect()
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

dict_literal_present_keys uses a BTreeMap<String, ()> purely for membership checks. A BTreeSet<String> (or similar set type) would better express intent and avoid storing unused unit values.

Copilot uses AI. Check for mistakes.
@Arths17
Copy link
Copy Markdown
Contributor

Arths17 commented Apr 5, 2026

Hey @asukaminato0721, thanks for putting this together! Discriminated union narrowing for TypedDict is a massive improvement for the LSP experience.

I took a look through the logic and I’d like to second the AI's feedback on two points to help get this 'merge-ready':

-Performance: In typed_dict_field_type_in_member, the linear scan on typed_dict_fields could definitely become a bottleneck for large projects since completions fire on every keystroke. Mapping these fields once before the narrowing loop would keep the LSP snappy.

-Idiomatic Rust: Switching the BTreeMap<String, ()> to a BTreeSet in dict_literal_present_keys would be much cleaner since we only care about membership.

Regarding the mypy_primer failure: I noticed check (7) is failing. If you need help debugging whether that’s a regression or just a flake, let me know I’m happy to pull the branch and help investigate!

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

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.

autocomplete for discriminated unions

3 participants