From 27d738c3dce8a9dae459231acbaabf1c0b8546ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:52:13 +0000 Subject: [PATCH 1/8] Initial plan From 4b270f94db5b645d3161f9b2b8cc2d86081067e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:25:06 +0000 Subject: [PATCH 2/8] Rename ContextFlagsCompletions to ContextFlagsIgnoreNodeInferences and add self-reference check for go-to-definition on object literal properties Port of TypeScript PR #62361: Make go to definition go to the constraint's properties for object literals in argument positions. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/jsx.go | 2 +- internal/checker/services.go | 33 +++++++++++++++++++++++++++++-- internal/checker/types.go | 2 +- internal/ls/completions.go | 8 ++++---- internal/ls/definition.go | 4 ++-- internal/ls/string_completions.go | 8 ++++---- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/internal/checker/jsx.go b/internal/checker/jsx.go index e3f92a1add9..49564ffb30f 100644 --- a/internal/checker/jsx.go +++ b/internal/checker/jsx.go @@ -225,7 +225,7 @@ func (c *Checker) getContextualTypeForJsxAttribute(attribute *ast.Node, contextF } func (c *Checker) getContextualJsxElementAttributesType(node *ast.Node, contextFlags ContextFlags) *Type { - if ast.IsJsxOpeningElement(node) && contextFlags != ContextFlagsCompletions { + if ast.IsJsxOpeningElement(node) && contextFlags != ContextFlagsIgnoreNodeInferences { index := c.findContextualNode(node.Parent, contextFlags == ContextFlagsNone) if index >= 0 { // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit diff --git a/internal/checker/services.go b/internal/checker/services.go index b71937dbea8..a1ed148afa1 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -310,7 +310,7 @@ func (c *Checker) shouldTreatPropertiesOfExternalModuleAsExports(resolvedExterna } func (c *Checker) GetContextualType(node *ast.Expression, contextFlags ContextFlags) *Type { - if contextFlags&ContextFlagsCompletions != 0 { + if contextFlags&ContextFlagsIgnoreNodeInferences != 0 { return runWithInferenceBlockedFromSourceNode(c, node, func() *Type { return c.getContextualType(node, contextFlags) }) } return c.getContextualType(node, contextFlags) @@ -802,8 +802,26 @@ func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { } func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node { + return c.getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral, name, nil) +} + +func (c *Checker) GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { + return c.getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral, name, propertyNameNode) +} + +func (c *Checker) getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { + result := c.getDeclarationsFromContextualType(objectLiteral, name, ContextFlagsNone) + if propertyNameNode != nil && hasSelfReferenceDeclaration(result, propertyNameNode) { + if withoutNodeInferences := c.getDeclarationsFromContextualType(objectLiteral, name, ContextFlagsIgnoreNodeInferences); len(withoutNodeInferences) > 0 { + result = withoutNodeInferences + } + } + return result +} + +func (c *Checker) getDeclarationsFromContextualType(objectLiteral *ast.Node, name string, contextFlags ContextFlags) []*ast.Node { var result []*ast.Node - if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil { + if t := c.getApparentTypeOfContextualType(objectLiteral, contextFlags); t != nil { for _, t := range t.Distributed() { prop := c.getPropertyOfType(t, name) if prop != nil { @@ -822,6 +840,17 @@ func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral return result } +// hasSelfReferenceDeclaration checks if any declaration points back to the same property +// in the object literal, indicating the contextual type was inferred from the node itself. +func hasSelfReferenceDeclaration(declarations []*ast.Node, propertyNameNode *ast.Node) bool { + for _, decl := range declarations { + if decl.Parent != nil && ast.IsObjectLiteralExpression(decl.Parent) && ast.IsObjectLiteralElement(decl) && decl.Name() == propertyNameNode { + return true + } + } + return false +} + // GetContextualTypeForArrayLiteralAtPosition returns the contextual type for an element at the given position // in an array with the given contextual type. func (c *Checker) GetContextualTypeForArrayLiteralAtPosition(contextualArrayType *Type, arrayLiteral *ast.Node, position int) *Type { diff --git a/internal/checker/types.go b/internal/checker/types.go index 3e2aafbd4c3..799ff3245eb 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -38,7 +38,7 @@ const ( ContextFlagsNone ContextFlags = 0 ContextFlagsSignature ContextFlags = 1 << 0 // Obtaining contextual signature ContextFlagsNoConstraints ContextFlags = 1 << 1 // Don't obtain type variable constraints - ContextFlagsCompletions ContextFlags = 1 << 2 // Ignore inference to current node and parent nodes out to the containing call for completions + ContextFlagsIgnoreNodeInferences ContextFlags = 1 << 2 // Ignore inference to current node and parent nodes out to the containing call for, for example, completions ContextFlagsSkipBindingPatterns ContextFlags = 1 << 3 // Ignore contextual types applied by binding patterns ) diff --git a/internal/ls/completions.go b/internal/ls/completions.go index b4759aa99e4..48ce4dcb82c 100644 --- a/internal/ls/completions.go +++ b/internal/ls/completions.go @@ -972,7 +972,7 @@ func (l *LanguageService) getCompletionData( } return globalsSearchContinue, nil } - completionsType := typeChecker.GetContextualType(objectLikeContainer, checker.ContextFlagsCompletions) + completionsType := typeChecker.GetContextualType(objectLikeContainer, checker.ContextFlagsIgnoreNodeInferences) t := core.IfElse(completionsType != nil, completionsType, instantiatedType) stringIndexType := typeChecker.GetStringIndexType(t) numberIndexType := typeChecker.GetNumberIndexType(t) @@ -1389,7 +1389,7 @@ func (l *LanguageService) getCompletionData( if attrsType == nil { return globalsSearchContinue, nil } - completionsType := typeChecker.GetContextualType(jsxContainer.Attributes(), checker.ContextFlagsCompletions) + completionsType := typeChecker.GetContextualType(jsxContainer.Attributes(), checker.ContextFlagsIgnoreNodeInferences) filteredSymbols, spreadMemberNames := filterJsxAttributes( getPropertiesForObjectExpression(attrsType, completionsType, jsxContainer.Attributes(), typeChecker), jsxContainer.Attributes().Properties(), @@ -2794,7 +2794,7 @@ func getContextualTypeForConditionalExpression(conditionalExpr *ast.Node, positi return typeChecker.GetContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex) } // Fall through to regular contextual type logic if not in an argument - contextualType := typeChecker.GetContextualType(conditionalExpr, checker.ContextFlagsCompletions) + contextualType := typeChecker.GetContextualType(conditionalExpr, checker.ContextFlagsIgnoreNodeInferences) if contextualType != nil { return contextualType } @@ -2882,7 +2882,7 @@ func getContextualType(previousToken *ast.Node, position int, file *ast.SourceFi // completion at `x ===/**/` return typeChecker.GetTypeAtLocation(parent.AsBinaryExpression().Left) } else { - contextualType := typeChecker.GetContextualType(previousToken, checker.ContextFlagsCompletions) + contextualType := typeChecker.GetContextualType(previousToken, checker.ContextFlagsIgnoreNodeInferences) if contextualType != nil { return contextualType } diff --git a/internal/ls/definition.go b/internal/ls/definition.go index 8e49d610243..8d957b29cbd 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -229,7 +229,7 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node } if symbol.Flags&(ast.SymbolFlagsProperty|ast.SymbolFlagsMethod|ast.SymbolFlagsAccessor) != 0 && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsObjectLiteral != 0 { if objectLiteral := core.FirstOrNil(symbol.Parent.Declarations); objectLiteral != nil { - if declarations := c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, symbol.Name); len(declarations) != 0 { + if declarations := c.GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral, symbol.Name, node); len(declarations) != 0 { return declarations } } @@ -262,7 +262,7 @@ func getDeclarationsFromObjectLiteralElement(c *checker.Checker, node *ast.Node) return nil } - return c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, name) + return c.GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral, name, node) } // Returns a CallLikeExpression where `node` is the target being invoked. diff --git a/internal/ls/string_completions.go b/internal/ls/string_completions.go index 76fdd624aac..af63a611e1e 100644 --- a/internal/ls/string_completions.go +++ b/internal/ls/string_completions.go @@ -281,7 +281,7 @@ func (l *LanguageService) getStringLiteralCompletionEntries( fromProperties: stringLiteralCompletionsForObjectLiteral(typeChecker, parent.Parent), } } - result := fromContextualType(checker.ContextFlagsCompletions, node, typeChecker) + result := fromContextualType(checker.ContextFlagsIgnoreNodeInferences, node, typeChecker) if result != nil { return &stringLiteralCompletions{ fromTypes: result, @@ -343,7 +343,7 @@ func (l *LanguageService) getStringLiteralCompletionEntries( return l.getStringLiteralCompletionsFromModuleNames(file, node, l.GetProgram(), typeChecker) case ast.KindCaseClause: tracker := newCaseClauseTracker(typeChecker, parent.Parent.AsCaseBlock().Clauses.Nodes) - contextualTypes := fromContextualType(checker.ContextFlagsCompletions, node, typeChecker) + contextualTypes := fromContextualType(checker.ContextFlagsIgnoreNodeInferences, node, typeChecker) if contextualTypes == nil { return nil } @@ -406,7 +406,7 @@ func (l *LanguageService) getStringLiteralCompletionEntries( fromTypes: fromContextualType(checker.ContextFlagsNone, node, typeChecker), } default: - result := fromContextualType(checker.ContextFlagsCompletions, node, typeChecker) + result := fromContextualType(checker.ContextFlagsIgnoreNodeInferences, node, typeChecker) if result != nil { return &stringLiteralCompletions{ fromTypes: result, @@ -515,7 +515,7 @@ func stringLiteralCompletionsForObjectLiteral( return nil } - completionsType := typeChecker.GetContextualType(objectLiteralExpression, checker.ContextFlagsCompletions) + completionsType := typeChecker.GetContextualType(objectLiteralExpression, checker.ContextFlagsIgnoreNodeInferences) symbols := getPropertiesForObjectExpression( contextualType, completionsType, From 6e5a7bdf2f69c1cd55ca13e542e5b9319b9fec40 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 10 Feb 2026 16:56:03 -0800 Subject: [PATCH 3/8] fmt --- internal/checker/types.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/checker/types.go b/internal/checker/types.go index 799ff3245eb..23b2f90f6c7 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -35,11 +35,11 @@ const ( type ContextFlags uint32 const ( - ContextFlagsNone ContextFlags = 0 - ContextFlagsSignature ContextFlags = 1 << 0 // Obtaining contextual signature - ContextFlagsNoConstraints ContextFlags = 1 << 1 // Don't obtain type variable constraints + ContextFlagsNone ContextFlags = 0 + ContextFlagsSignature ContextFlags = 1 << 0 // Obtaining contextual signature + ContextFlagsNoConstraints ContextFlags = 1 << 1 // Don't obtain type variable constraints ContextFlagsIgnoreNodeInferences ContextFlags = 1 << 2 // Ignore inference to current node and parent nodes out to the containing call for, for example, completions - ContextFlagsSkipBindingPatterns ContextFlags = 1 << 3 // Ignore contextual types applied by binding patterns + ContextFlagsSkipBindingPatterns ContextFlags = 1 << 3 // Ignore contextual types applied by binding patterns ) type TypeFormatFlags uint32 From 81417290620630a4934500e0821a58b34a08410d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 01:31:46 +0000 Subject: [PATCH 4/8] Fix self-reference detection: use public GetContextualType with IgnoreNodeInferences for proper inference blocking, update baselines The self-reference check was correctly detecting when property declarations pointed back to the same object literal, but the retry with IgnoreNodeInferences was using the internal getApparentTypeOfContextualType which doesn't run through runWithInferenceBlockedFromSourceNode. Changed to use the public GetContextualType API which properly blocks inference from the source node, matching TypeScript's behavior. Baselines for goToDefinitionObjectLiteralProperties2 and goToDefinitionObjectLiteralProperties3 now match TypeScript's output exactly (diff files deleted). Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/services.go | 37 ++++++++++------ ...ionObjectLiteralProperties2.baseline.jsonc | 19 +++++++- ...jectLiteralProperties2.baseline.jsonc.diff | 43 ------------------- ...ionObjectLiteralProperties3.baseline.jsonc | 4 +- ...jectLiteralProperties3.baseline.jsonc.diff | 18 -------- 5 files changed, 43 insertions(+), 78 deletions(-) delete mode 100644 testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc.diff delete mode 100644 testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc.diff diff --git a/internal/checker/services.go b/internal/checker/services.go index a1ed148afa1..fb084536870 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -812,8 +812,13 @@ func (c *Checker) GetContextualDeclarationsForObjectLiteralElementWithNode(objec func (c *Checker) getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { result := c.getDeclarationsFromContextualType(objectLiteral, name, ContextFlagsNone) if propertyNameNode != nil && hasSelfReferenceDeclaration(result, propertyNameNode) { - if withoutNodeInferences := c.getDeclarationsFromContextualType(objectLiteral, name, ContextFlagsIgnoreNodeInferences); len(withoutNodeInferences) > 0 { - result = withoutNodeInferences + // Use the public GetContextualType with IgnoreNodeInferences to run through + // runWithInferenceBlockedFromSourceNode, which blocks inference from the current node + withoutNodeInferencesType := c.GetContextualType(objectLiteral, ContextFlagsIgnoreNodeInferences) + if withoutNodeInferencesType != nil { + if withoutNodeInferences := c.getDeclarationsFromType(withoutNodeInferencesType, name); len(withoutNodeInferences) > 0 { + result = withoutNodeInferences + } } } return result @@ -822,17 +827,23 @@ func (c *Checker) getContextualDeclarationsForObjectLiteralElementWorker(objectL func (c *Checker) getDeclarationsFromContextualType(objectLiteral *ast.Node, name string, contextFlags ContextFlags) []*ast.Node { var result []*ast.Node if t := c.getApparentTypeOfContextualType(objectLiteral, contextFlags); t != nil { - for _, t := range t.Distributed() { - prop := c.getPropertyOfType(t, name) - if prop != nil { - for _, declaration := range prop.Declarations { - result = core.AppendIfUnique(result, declaration) - } - } else { - for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { - if info.declaration != nil { - result = core.AppendIfUnique(result, info.declaration) - } + result = c.getDeclarationsFromType(t, name) + } + return result +} + +func (c *Checker) getDeclarationsFromType(t *Type, name string) []*ast.Node { + var result []*ast.Node + for _, t := range t.Distributed() { + prop := c.getPropertyOfType(t, name) + if prop != nil { + for _, declaration := range prop.Declarations { + result = core.AppendIfUnique(result, declaration) + } + } else { + for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { + if info.declaration != nil { + result = core.AppendIfUnique(result, info.declaration) } } } diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc index 818d0381532..26eadd91106 100644 --- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc +++ b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc @@ -37,11 +37,18 @@ // === goToDefinition === // === /goToDefinitionObjectLiteralProperties2.ts === +// type C = { +// <|[|foo|]: string;|> +// bar: number; +// }; +// +// --- (line: 6) skipped --- + // --- (line: 10) skipped --- // }); // // const result = fn({ -// <|[|foo|]/*GOTO DEF*/: ""|>, +// foo/*GOTO DEF*/: "", // bar: 1, // }); // @@ -52,11 +59,19 @@ // === goToDefinition === // === /goToDefinitionObjectLiteralProperties2.ts === +// type C = { +// foo: string; +// <|[|bar|]: number;|> +// }; +// +// declare function fn(arg: T): T; +// --- (line: 7) skipped --- + // --- (line: 11) skipped --- // // const result = fn({ // foo: "", -// <|[|bar|]/*GOTO DEF*/: 1|>, +// bar/*GOTO DEF*/: 1, // }); // // // this one shouldn't go to the constraint type diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc.diff deleted file mode 100644 index f0519212c83..00000000000 --- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties2.baseline.jsonc.diff +++ /dev/null @@ -1,43 +0,0 @@ ---- old.goToDefinitionObjectLiteralProperties2.baseline.jsonc -+++ new.goToDefinitionObjectLiteralProperties2.baseline.jsonc -@@= skipped -36, +36 lines =@@ - - // === goToDefinition === - // === /goToDefinitionObjectLiteralProperties2.ts === --// type C = { --// <|[|foo|]: string;|> --// bar: number; --// }; --// --// --- (line: 6) skipped --- -- - // --- (line: 10) skipped --- - // }); - // - // const result = fn({ --// foo/*GOTO DEF*/: "", -+// <|[|foo|]/*GOTO DEF*/: ""|>, - // bar: 1, - // }); - // -@@= skipped -22, +15 lines =@@ - - // === goToDefinition === - // === /goToDefinitionObjectLiteralProperties2.ts === --// type C = { --// foo: string; --// <|[|bar|]: number;|> --// }; --// --// declare function fn(arg: T): T; --// --- (line: 7) skipped --- -- - // --- (line: 11) skipped --- - // - // const result = fn({ - // foo: "", --// bar/*GOTO DEF*/: 1, -+// <|[|bar|]/*GOTO DEF*/: 1|>, - // }); - // - // // this one shouldn't go to the constraint type \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc index 8bdd19a63b6..02596c81af2 100644 --- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc +++ b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc @@ -23,7 +23,7 @@ // === goToDefinition === // === /goToDefinitionObjectLiteralProperties3.ts === // type A = { -// foo: unknown; +// <|[|foo|]: unknown;|> // }; // // type B = { @@ -37,5 +37,5 @@ // function test2(arg: T | B) {} // // test2({ -// <|[|foo|]/*GOTO DEF*/: 2|>, +// foo/*GOTO DEF*/: 2, // }); \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc.diff deleted file mode 100644 index cc0f356a38c..00000000000 --- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/goToDefinitionObjectLiteralProperties3.baseline.jsonc.diff +++ /dev/null @@ -1,18 +0,0 @@ ---- old.goToDefinitionObjectLiteralProperties3.baseline.jsonc -+++ new.goToDefinitionObjectLiteralProperties3.baseline.jsonc -@@= skipped -22, +22 lines =@@ - // === goToDefinition === - // === /goToDefinitionObjectLiteralProperties3.ts === - // type A = { --// <|[|foo|]: unknown;|> -+// foo: unknown; - // }; - // - // type B = { -@@= skipped -14, +14 lines =@@ - // function test2(arg: T | B) {} - // - // test2({ --// foo/*GOTO DEF*/: 2, -+// <|[|foo|]/*GOTO DEF*/: 2|>, - // }); \ No newline at end of file From fe5e20d709a9ed0453e3e90dd815342c4a50d032 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 01:43:28 +0000 Subject: [PATCH 5/8] Simplify GetContextualDeclarationsForObjectLiteralElement: inline all helper functions Remove unnecessary wrapper functions (GetContextualDeclarationsForObjectLiteralElementWithNode, getContextualDeclarationsForObjectLiteralElementWorker, getDeclarationsFromContextualType, getDeclarationsFromType, hasSelfReferenceDeclaration) and put the logic directly in GetContextualDeclarationsForObjectLiteralElement with the added propertyNameNode parameter. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/services.go | 82 ++++++++++++++---------------------- internal/ls/definition.go | 4 +- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/internal/checker/services.go b/internal/checker/services.go index fb084536870..4f0c5e6e6ba 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -801,67 +801,47 @@ func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { return t } -func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node { - return c.getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral, name, nil) -} - -func (c *Checker) GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { - return c.getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral, name, propertyNameNode) -} - -func (c *Checker) getContextualDeclarationsForObjectLiteralElementWorker(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { - result := c.getDeclarationsFromContextualType(objectLiteral, name, ContextFlagsNone) - if propertyNameNode != nil && hasSelfReferenceDeclaration(result, propertyNameNode) { - // Use the public GetContextualType with IgnoreNodeInferences to run through - // runWithInferenceBlockedFromSourceNode, which blocks inference from the current node - withoutNodeInferencesType := c.GetContextualType(objectLiteral, ContextFlagsIgnoreNodeInferences) - if withoutNodeInferencesType != nil { - if withoutNodeInferences := c.getDeclarationsFromType(withoutNodeInferencesType, name); len(withoutNodeInferences) > 0 { - result = withoutNodeInferences +func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { + var result []*ast.Node + if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil { + for _, t := range t.Distributed() { + prop := c.getPropertyOfType(t, name) + if prop != nil { + for _, declaration := range prop.Declarations { + result = core.AppendIfUnique(result, declaration) + } + } else { + for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { + if info.declaration != nil { + result = core.AppendIfUnique(result, info.declaration) + } + } } } } - return result -} - -func (c *Checker) getDeclarationsFromContextualType(objectLiteral *ast.Node, name string, contextFlags ContextFlags) []*ast.Node { - var result []*ast.Node - if t := c.getApparentTypeOfContextualType(objectLiteral, contextFlags); t != nil { - result = c.getDeclarationsFromType(t, name) - } - return result -} - -func (c *Checker) getDeclarationsFromType(t *Type, name string) []*ast.Node { - var result []*ast.Node - for _, t := range t.Distributed() { - prop := c.getPropertyOfType(t, name) - if prop != nil { - for _, declaration := range prop.Declarations { - result = core.AppendIfUnique(result, declaration) - } - } else { - for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { - if info.declaration != nil { - result = core.AppendIfUnique(result, info.declaration) + if propertyNameNode != nil && core.Some(result, func(decl *ast.Node) bool { + return decl.Parent != nil && ast.IsObjectLiteralExpression(decl.Parent) && ast.IsObjectLiteralElement(decl) && decl.Name() == propertyNameNode + }) { + // Use the public GetContextualType with IgnoreNodeInferences to run through + // runWithInferenceBlockedFromSourceNode, which blocks inference from the current node + if withoutNodeInferencesType := c.GetContextualType(objectLiteral, ContextFlagsIgnoreNodeInferences); withoutNodeInferencesType != nil { + var withoutNodeInferences []*ast.Node + for _, t := range withoutNodeInferencesType.Distributed() { + prop := c.getPropertyOfType(t, name) + if prop != nil { + for _, declaration := range prop.Declarations { + withoutNodeInferences = core.AppendIfUnique(withoutNodeInferences, declaration) + } } } + if len(withoutNodeInferences) > 0 { + result = withoutNodeInferences + } } } return result } -// hasSelfReferenceDeclaration checks if any declaration points back to the same property -// in the object literal, indicating the contextual type was inferred from the node itself. -func hasSelfReferenceDeclaration(declarations []*ast.Node, propertyNameNode *ast.Node) bool { - for _, decl := range declarations { - if decl.Parent != nil && ast.IsObjectLiteralExpression(decl.Parent) && ast.IsObjectLiteralElement(decl) && decl.Name() == propertyNameNode { - return true - } - } - return false -} - // GetContextualTypeForArrayLiteralAtPosition returns the contextual type for an element at the given position // in an array with the given contextual type. func (c *Checker) GetContextualTypeForArrayLiteralAtPosition(contextualArrayType *Type, arrayLiteral *ast.Node, position int) *Type { diff --git a/internal/ls/definition.go b/internal/ls/definition.go index 8d957b29cbd..d24cb8545a7 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -229,7 +229,7 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node } if symbol.Flags&(ast.SymbolFlagsProperty|ast.SymbolFlagsMethod|ast.SymbolFlagsAccessor) != 0 && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsObjectLiteral != 0 { if objectLiteral := core.FirstOrNil(symbol.Parent.Declarations); objectLiteral != nil { - if declarations := c.GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral, symbol.Name, node); len(declarations) != 0 { + if declarations := c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, symbol.Name, node); len(declarations) != 0 { return declarations } } @@ -262,7 +262,7 @@ func getDeclarationsFromObjectLiteralElement(c *checker.Checker, node *ast.Node) return nil } - return c.GetContextualDeclarationsForObjectLiteralElementWithNode(objectLiteral, name, node) + return c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, name, node) } // Returns a CallLikeExpression where `node` is the target being invoked. From a3a95aabe7cffb04bc7c320e770c82a2fce15ff0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:07:28 +0000 Subject: [PATCH 6/8] Move self-reference detection to definition.go using GetPropertySymbolsFromContextualType Revert GetContextualDeclarationsForObjectLiteralElement to its original form. Move the self-reference detection logic from the checker to definition.go's getDeclarationsFromObjectLiteralElement, matching the TypeScript PR pattern: 1. Use GetContextualType (public API) to get the contextual type 2. Use GetPropertySymbolsFromContextualType to get property symbols (handles union discriminant filtering, deduplication, etc.) 3. Check if any property symbol's valueDeclaration self-references 4. If so, retry with ContextFlagsIgnoreNodeInferences 5. Call getDeclarationsFromObjectLiteralElement from the object literal property check path in getDeclarationsFromLocation Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/services.go | 22 +--------------------- internal/ls/definition.go | 33 ++++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/internal/checker/services.go b/internal/checker/services.go index 4f0c5e6e6ba..ad1b7727d2e 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -801,7 +801,7 @@ func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { return t } -func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string, propertyNameNode *ast.Node) []*ast.Node { +func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node { var result []*ast.Node if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil { for _, t := range t.Distributed() { @@ -819,26 +819,6 @@ func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral } } } - if propertyNameNode != nil && core.Some(result, func(decl *ast.Node) bool { - return decl.Parent != nil && ast.IsObjectLiteralExpression(decl.Parent) && ast.IsObjectLiteralElement(decl) && decl.Name() == propertyNameNode - }) { - // Use the public GetContextualType with IgnoreNodeInferences to run through - // runWithInferenceBlockedFromSourceNode, which blocks inference from the current node - if withoutNodeInferencesType := c.GetContextualType(objectLiteral, ContextFlagsIgnoreNodeInferences); withoutNodeInferencesType != nil { - var withoutNodeInferences []*ast.Node - for _, t := range withoutNodeInferencesType.Distributed() { - prop := c.getPropertyOfType(t, name) - if prop != nil { - for _, declaration := range prop.Declarations { - withoutNodeInferences = core.AppendIfUnique(withoutNodeInferences, declaration) - } - } - } - if len(withoutNodeInferences) > 0 { - result = withoutNodeInferences - } - } - } return result } diff --git a/internal/ls/definition.go b/internal/ls/definition.go index d24cb8545a7..15df1b5a791 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -228,12 +228,19 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node } } if symbol.Flags&(ast.SymbolFlagsProperty|ast.SymbolFlagsMethod|ast.SymbolFlagsAccessor) != 0 && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsObjectLiteral != 0 { + if declarations := getDeclarationsFromObjectLiteralElement(c, node); len(declarations) != 0 { + return declarations + } if objectLiteral := core.FirstOrNil(symbol.Parent.Declarations); objectLiteral != nil { - if declarations := c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, symbol.Name, node); len(declarations) != 0 { + if declarations := c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, symbol.Name); len(declarations) != 0 { return declarations } } } + objectLiteralElementDeclarations := getDeclarationsFromObjectLiteralElement(c, node) + if len(objectLiteralElementDeclarations) > 0 { + return objectLiteralElementDeclarations + } return symbol.Declarations } if indexInfos := c.GetIndexSignaturesAtLocation(node); len(indexInfos) != 0 { @@ -250,19 +257,27 @@ func getDeclarationsFromObjectLiteralElement(c *checker.Checker, node *ast.Node) return nil } - // Get the contextual type of the object literal - objectLiteral := element.Parent - if objectLiteral == nil || !ast.IsObjectLiteralExpression(objectLiteral) { + contextualType := c.GetContextualType(element.Parent, checker.ContextFlagsNone) + if contextualType == nil { return nil } - // Get the name of the property - name := ast.GetTextOfPropertyName(element.Name()) - if name == "" { - return nil + properties := c.GetPropertySymbolsFromContextualType(element, contextualType, false /*unionSymbolOk*/) + if core.Some(properties, func(p *ast.Symbol) bool { + return p.ValueDeclaration != nil && ast.IsObjectLiteralExpression(p.ValueDeclaration.Parent) && ast.IsObjectLiteralElement(p.ValueDeclaration) && p.ValueDeclaration.Name() == node + }) { + if withoutNodeInferencesType := c.GetContextualType(element.Parent, checker.ContextFlagsIgnoreNodeInferences); withoutNodeInferencesType != nil { + if withoutNodeInferencesProperties := c.GetPropertySymbolsFromContextualType(element, withoutNodeInferencesType, false /*unionSymbolOk*/); len(withoutNodeInferencesProperties) > 0 { + properties = withoutNodeInferencesProperties + } + } } - return c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, name, node) + var result []*ast.Node + for _, prop := range properties { + result = append(result, prop.Declarations...) + } + return result } // Returns a CallLikeExpression where `node` is the target being invoked. From 01b3e9c32c2c3104fc7d1d9fcb2d025e4ddd2ad7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:09:31 +0000 Subject: [PATCH 7/8] Remove duplicate getDeclarationsFromObjectLiteralElement call Use else-if to avoid calling getDeclarationsFromObjectLiteralElement twice when the symbol is an object literal property. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/definition.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/ls/definition.go b/internal/ls/definition.go index 15df1b5a791..9227782883b 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -236,10 +236,8 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node return declarations } } - } - objectLiteralElementDeclarations := getDeclarationsFromObjectLiteralElement(c, node) - if len(objectLiteralElementDeclarations) > 0 { - return objectLiteralElementDeclarations + } else if declarations := getDeclarationsFromObjectLiteralElement(c, node); len(declarations) > 0 { + return declarations } return symbol.Declarations } From 470a967576c184a324b46850e1b1a6ca06e74701 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:29:23 +0000 Subject: [PATCH 8/8] Remove GetContextualDeclarationsForObjectLiteralElement; match TS flow Remove GetContextualDeclarationsForObjectLiteralElement from checker/services.go as it has no equivalent in the original TypeScript code. Simplify getDeclarationsFromLocation to always call getDeclarationsFromObjectLiteralElement (matching TS's getDefinitionFromObjectLiteralElement), falling back to symbol.Declarations. This matches the TypeScript flow where getDefinitionFromObjectLiteralElement is called for all symbols. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/services.go | 21 --------------------- internal/ls/definition.go | 14 +++----------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/internal/checker/services.go b/internal/checker/services.go index ad1b7727d2e..a9f0ddec965 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -801,27 +801,6 @@ func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { return t } -func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node { - var result []*ast.Node - if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil { - for _, t := range t.Distributed() { - prop := c.getPropertyOfType(t, name) - if prop != nil { - for _, declaration := range prop.Declarations { - result = core.AppendIfUnique(result, declaration) - } - } else { - for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { - if info.declaration != nil { - result = core.AppendIfUnique(result, info.declaration) - } - } - } - } - } - return result -} - // GetContextualTypeForArrayLiteralAtPosition returns the contextual type for an element at the given position // in an array with the given contextual type. func (c *Checker) GetContextualTypeForArrayLiteralAtPosition(contextualArrayType *Type, arrayLiteral *ast.Node, position int) *Type { diff --git a/internal/ls/definition.go b/internal/ls/definition.go index 9227782883b..732e5e6f664 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -227,17 +227,9 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node symbol = resolved } } - if symbol.Flags&(ast.SymbolFlagsProperty|ast.SymbolFlagsMethod|ast.SymbolFlagsAccessor) != 0 && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsObjectLiteral != 0 { - if declarations := getDeclarationsFromObjectLiteralElement(c, node); len(declarations) != 0 { - return declarations - } - if objectLiteral := core.FirstOrNil(symbol.Parent.Declarations); objectLiteral != nil { - if declarations := c.GetContextualDeclarationsForObjectLiteralElement(objectLiteral, symbol.Name); len(declarations) != 0 { - return declarations - } - } - } else if declarations := getDeclarationsFromObjectLiteralElement(c, node); len(declarations) > 0 { - return declarations + objectLiteralElementDeclarations := getDeclarationsFromObjectLiteralElement(c, node) + if len(objectLiteralElementDeclarations) > 0 { + return objectLiteralElementDeclarations } return symbol.Declarations }