Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions internal/fourslash/_scripts/failingTests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ TestCompletionListInvalidMemberNames2
TestCompletionListInvalidMemberNames_escapeQuote
TestCompletionListInvalidMemberNames_startWithSpace
TestCompletionListInvalidMemberNames_withExistingIdentifier
TestCompletionListObjectMembersInTypeLocationWithTypeof
TestCompletionListOfGenericSymbol
TestCompletionListOnAliases
TestCompletionListStringParenthesizedExpression
Expand Down Expand Up @@ -251,7 +250,6 @@ TestGetJavaScriptQuickInfo7
TestGetJavaScriptQuickInfo8
TestGetJavaScriptSyntacticDiagnostics24
TestGetOccurrencesIfElseBroken
TestGetQuickInfoForIntersectionTypes
TestHoverOverComment
TestImportCompletionsPackageJsonExportsSpecifierEndsInTs
TestImportCompletionsPackageJsonExportsTrailingSlash1
Expand Down Expand Up @@ -425,7 +423,6 @@ TestQuickInfoJSDocFunctionThis
TestQuickInfoJSExport
TestQuickInfoJsDocGetterSetterNoCrash1
TestQuickInfoJsDocNonDiscriminatedUnionSharedProp
TestQuickInfoJsPropertyAssignedAfterMethodDeclaration
TestQuickInfoJsdocTypedefMissingType
TestQuickInfoMappedSpreadTypes
TestQuickInfoMappedType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func TestCompletionListObjectMembersInTypeLocationWithTypeof(t *testing.T) {
t.Parallel()
t.Skip()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
const languageService = { getCompletions() {} }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestGetQuickInfoForIntersectionTypes(t *testing.T) {
t.Parallel()
t.Skip()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `function f(): string & {(): any} {
return <any>{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestQuickInfoJsPropertyAssignedAfterMethodDeclaration(t *testing.T) {
t.Parallel()
t.Skip()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @noLib: true
// @allowJs: true
Expand Down
27 changes: 19 additions & 8 deletions internal/ls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
}
}
flags := symbol.Flags
if flags&ast.SymbolFlagsProperty != 0 && declaration != nil && ast.IsMethodDeclaration(declaration) {
flags = ast.SymbolFlagsMethod
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

The flags reassignment overwrites all flags with only SymbolFlagsMethod, losing other potentially important flag information. Consider using bitwise operations to add the Method flag while preserving other flags: flags = (flags &^ ast.SymbolFlagsProperty) | ast.SymbolFlagsMethod.

Suggested change
flags = ast.SymbolFlagsMethod
flags = (flags &^ ast.SymbolFlagsProperty) | ast.SymbolFlagsMethod

Copilot uses AI. Check for mistakes.
}
if flags&ast.SymbolFlagsType != 0 && (ast.IsPartOfTypeNode(node) || ast.IsTypeDeclarationName(node)) {
// If the symbol has a type meaning and we're in a type context, remove value-only meanings
flags &^= ast.SymbolFlagsVariable | ast.SymbolFlagsFunction
Expand Down Expand Up @@ -165,7 +168,11 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
}
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
b.WriteString(": ")
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
if callNode := getCallOrNewExpression(node); callNode != nil {
b.WriteString(c.SignatureToStringEx(c.GetResolvedSignature(callNode), container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature|checker.TypeFormatFlagsWriteArrowStyleSignature))
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

GetResolvedSignature may return nil if signature resolution fails. This would cause SignatureToStringEx to receive a nil signature, which could lead to a panic or incorrect output. Add a nil check: if sig := c.GetResolvedSignature(callNode); sig != nil { ... } else { /* fall back to type display */ }.

Suggested change
b.WriteString(c.SignatureToStringEx(c.GetResolvedSignature(callNode), container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature|checker.TypeFormatFlagsWriteArrowStyleSignature))
if sig := c.GetResolvedSignature(callNode); sig != nil {
b.WriteString(c.SignatureToStringEx(sig, container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature|checker.TypeFormatFlagsWriteArrowStyleSignature))
} else {
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
}

Copilot uses AI. Check for mistakes.
} else {
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
}
case flags&ast.SymbolFlagsEnumMember != 0:
b.WriteString("(enum member) ")
t := c.GetTypeOfSymbol(symbol)
Expand All @@ -176,22 +183,26 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
}
case flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0:
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
if len(signatures) == 1 && signatures[0].Declaration() != nil {
declaration = signatures[0].Declaration()
if len(signatures) == 1 {
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
declaration = d
}
}
prefix := core.IfElse(symbol.Flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
prefix := core.IfElse(flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
writeSignatures(&b, c, signatures, container, prefix, symbol)
case flags&ast.SymbolFlagsConstructor != 0:
signatures := getSignaturesAtLocation(c, symbol.Parent, checker.SignatureKindConstruct, node)
if len(signatures) == 1 && signatures[0].Declaration() != nil {
declaration = signatures[0].Declaration()
if len(signatures) == 1 {
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
declaration = d
}
}
writeSignatures(&b, c, signatures, container, "constructor ", symbol.Parent)
case flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
b.WriteString("this")
} else {
b.WriteString(core.IfElse(symbol.Flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
b.WriteString(core.IfElse(flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
writeTypeParams(&b, c, params)
Expand Down Expand Up @@ -288,7 +299,7 @@ func getCallOrNewExpression(node *ast.Node) *ast.Node {
if ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node {
node = node.Parent
}
if ast.IsCallExpression(node.Parent) || ast.IsNewExpression(node.Parent) {
if (ast.IsCallExpression(node.Parent) || ast.IsNewExpression(node.Parent)) && node.Parent.Expression() == node {
return node.Parent
}
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | const h: { (a: string): number; (a: number): string; }
// | const h: (a: number) => string
// | ```
// |
// | ----------------------------------------------------------------------
// h("hello");
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | const h: { (a: string): number; (a: number): string; }
// | const h: (a: string) => number
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -525,7 +525,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nconst h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nconst h: (a: number) => string\n```\n"
},
"range": {
"start": {
Expand All @@ -552,7 +552,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nconst h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nconst h: (a: string) => number\n```\n"
},
"range": {
"start": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
// ^^^^^^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iInstance: I
// | var iInstance: () => string
// | ```
// |
// | ----------------------------------------------------------------------
Expand All @@ -69,7 +69,7 @@
// ^^^^^^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iInstance: I
// | var iInstance: () => I
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -249,7 +249,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iInstance: I\n```\n"
"value": "```tsx\nvar iInstance: () => string\n```\n"
},
"range": {
"start": {
Expand Down Expand Up @@ -303,7 +303,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iInstance: I\n```\n"
"value": "```tsx\nvar iInstance: () => I\n```\n"
},
"range": {
"start": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | let h: { (a: string): number; (a: number): string; }
// | let h: (a: number) => string
// | ```
// |
// | ----------------------------------------------------------------------
// h("hello");
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | let h: { (a: string): number; (a: number): string; }
// | let h: (a: string) => number
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -525,7 +525,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nlet h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nlet h: (a: number) => string\n```\n"
},
"range": {
"start": {
Expand All @@ -552,7 +552,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nlet h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nlet h: (a: string) => number\n```\n"
},
"range": {
"start": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,15 @@
// ^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iVal: I<string>
// | var iVal: <"hello">(a: "hello", b: string) => "hello"
// | ```
// |
// | ----------------------------------------------------------------------
// iVal("hello", "hello");
// ^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iVal: I<string>
// | var iVal: <"hello">(a: "hello", b: string) => "hello"
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -404,7 +404,7 @@
// ^^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iVal1: I1<I<string>>
// | var iVal1: <I<string>>(a: I<string>, b: I<string>) => I<string>
// | ```
// |
// | ----------------------------------------------------------------------
Expand All @@ -426,7 +426,7 @@
// ^^^^^
// | ----------------------------------------------------------------------
// | ```tsx
// | var iVal1: I1<I<string>>
// | var iVal1: <I<string>>(a: I<string>, b: I<string>) => I<string>
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -1108,7 +1108,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iVal: I<string>\n```\n"
"value": "```tsx\nvar iVal: <\"hello\">(a: \"hello\", b: string) => \"hello\"\n```\n"
},
"range": {
"start": {
Expand All @@ -1135,7 +1135,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iVal: I<string>\n```\n"
"value": "```tsx\nvar iVal: <\"hello\">(a: \"hello\", b: string) => \"hello\"\n```\n"
},
"range": {
"start": {
Expand Down Expand Up @@ -1972,7 +1972,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iVal1: I1<I<string>>\n```\n"
"value": "```tsx\nvar iVal1: <I<string>>(a: I<string>, b: I<string>) => I<string>\n```\n"
},
"range": {
"start": {
Expand Down Expand Up @@ -2053,7 +2053,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar iVal1: I1<I<string>>\n```\n"
"value": "```tsx\nvar iVal1: <I<string>>(a: I<string>, b: I<string>) => I<string>\n```\n"
},
"range": {
"start": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | var h: { (a: string): number; (a: number): string; }
// | var h: (a: number) => string
// | ```
// |
// | ----------------------------------------------------------------------
// h("hello");
// ^
// | ----------------------------------------------------------------------
// | ```tsx
// | var h: { (a: string): number; (a: number): string; }
// | var h: (a: string) => number
// | ```
// |
// | ----------------------------------------------------------------------
Expand Down Expand Up @@ -451,7 +451,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nvar h: (a: number) => string\n```\n"
},
"range": {
"start": {
Expand All @@ -478,7 +478,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar h: { (a: string): number; (a: number): string; }\n```\n"
"value": "```tsx\nvar h: (a: string) => number\n```\n"
},
"range": {
"start": {
Expand Down