diff --git a/internal/fourslash/tests/hoverNilBaseSymbolIntersection_test.go b/internal/fourslash/tests/hoverNilBaseSymbolIntersection_test.go new file mode 100644 index 0000000000..c7a14e472a --- /dev/null +++ b/internal/fourslash/tests/hoverNilBaseSymbolIntersection_test.go @@ -0,0 +1,32 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestHoverNilBaseSymbolIntersection(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + + const content = ` +// @strict: true +// @filename: main.ts + +class Base {} + +declare const BaseFactory: new() => Base & { c: string }; + +class Derived extends BaseFactory { + static /*1*/idField = "id" as const; +} +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + + // We only care that hover/quickinfo does not crash (panic) when baseType.Symbol() is nil. + // Pre-fix (#2763), hovering on the static property could panic in getJSDocOrTag. + f.VerifyBaselineHover(t) +} diff --git a/internal/ls/hover.go b/internal/ls/hover.go index 471c45de98..92af1a2973 100644 --- a/internal/ls/hover.go +++ b/internal/ls/hover.go @@ -509,7 +509,7 @@ func getJSDocOrTag(c *checker.Checker, node *ast.Node) *ast.Node { isStatic := ast.HasStaticModifier(node) for _, baseType := range c.GetBaseTypes(c.GetDeclaredTypeOfSymbol(node.Parent.Symbol())) { t := baseType - if isStatic { + if isStatic && baseType.Symbol() != nil { t = c.GetTypeOfSymbol(baseType.Symbol()) } if prop := c.GetPropertyOfType(t, symbol.Name); prop != nil && prop.ValueDeclaration != nil { diff --git a/testdata/baselines/reference/fourslash/quickInfo/hoverNilBaseSymbolIntersection.baseline b/testdata/baselines/reference/fourslash/quickInfo/hoverNilBaseSymbolIntersection.baseline new file mode 100644 index 0000000000..3d4098eb59 --- /dev/null +++ b/testdata/baselines/reference/fourslash/quickInfo/hoverNilBaseSymbolIntersection.baseline @@ -0,0 +1,46 @@ +// === QuickInfo === +=== /main.ts === +// class Base {} +// +// declare const BaseFactory: new() => Base & { c: string }; +// +// class Derived extends BaseFactory { +// static idField = "id" as const; +// ^^^^^^^ +// | ---------------------------------------------------------------------- +// | ```tsx +// | (property) Derived.idField: "id" +// | ``` +// | +// | ---------------------------------------------------------------------- +// } +// +[ + { + "marker": { + "Position": 119, + "LSPosition": { + "line": 5, + "character": 9 + }, + "Name": "1", + "Data": {} + }, + "item": { + "contents": { + "kind": "markdown", + "value": "```tsx\n(property) Derived.idField: \"id\"\n```\n" + }, + "range": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 16 + } + } + } + } +] \ No newline at end of file