Skip to content

[ty] Better class def completions#22571

Merged
BurntSushi merged 2 commits intoastral-sh:mainfrom
RasmusNygren:better-class-def-completions
Jan 15, 2026
Merged

[ty] Better class def completions#22571
BurntSushi merged 2 commits intoastral-sh:mainfrom
RasmusNygren:better-class-def-completions

Conversation

@RasmusNygren
Copy link
Contributor

Summary

Prefer completions of type ClassLiteral inside class-def context.

Fixes astral-sh/ty#1776

Test Plan

New completion-eval test that has incorrect ranking on main but correct after this patch.

We also rank `Protocol`, `TypedDict`, `NamedTuple`
and `Generic` equal to `ClassLiteral` as they are of
type `SpecialForm` but can still be inherited from.
@AlexWaygood AlexWaygood added server Related to the LSP server ty Multi-file analysis & type inference labels Jan 14, 2026
Comment on lines +841 to +853
fn is_in_class_def(&self) -> bool {
for node in self.covering_node.ancestors() {
if let ast::AnyNodeRef::StmtClassDef(class_def) = node {
return class_def
.arguments
.as_ref()
.is_some_and(|args| args.range.contains_range(self.range));
}
if node.is_statement() {
return false;
}
}
false
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This leads to us calculating whether or not we're in a class def twice, here and in add_argument_completions. I think this is fine for now but we probably want to do some refactors here and store more information on the context such that we only run the detection once and can re-use that both to add completions and for ranking. Perhaps the NonImportContext can store another enum and track if we're in a class-def/raising_exception etc.

Have you already thought about how to best scale this @BurntSushi ?

Copy link
Member

Choose a reason for hiding this comment

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

I haven't thought too much about this, no. But what you say sounds plausible to me. Possibly the challenge here is making sure we don't try to pre-compute too much stuff, particularly if we end up not needing it.

But yeah I agree that this is fine for now. Especially given that covering_node is only computed once now. :-)

@MichaReiser MichaReiser requested review from BurntSushi and removed request for carljm, dcreager and sharkdp January 14, 2026 20:47
Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

Beautiful. This looks great as-is, thank you!!!

Comment on lines +841 to +853
fn is_in_class_def(&self) -> bool {
for node in self.covering_node.ancestors() {
if let ast::AnyNodeRef::StmtClassDef(class_def) = node {
return class_def
.arguments
.as_ref()
.is_some_and(|args| args.range.contains_range(self.range));
}
if node.is_statement() {
return false;
}
}
false
Copy link
Member

Choose a reason for hiding this comment

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

I haven't thought too much about this, no. But what you say sounds plausible to me. Possibly the challenge here is making sure we don't try to pre-compute too much stuff, particularly if we end up not needing it.

But yeah I agree that this is fine for now. Especially given that covering_node is only computed once now. :-)

@BurntSushi BurntSushi merged commit 2a29ce3 into astral-sh:main Jan 15, 2026
47 checks passed
@RasmusNygren RasmusNygren deleted the better-class-def-completions branch January 15, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[completions] Rank symbols with class-literal types higher inside class declarations

3 participants