diff --git a/internal/fourslash/tests/completionJSDocBeforeProperty_test.go b/internal/fourslash/tests/completionJSDocBeforeProperty_test.go new file mode 100644 index 0000000000..8aeb802d88 --- /dev/null +++ b/internal/fourslash/tests/completionJSDocBeforeProperty_test.go @@ -0,0 +1,49 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +// Test for: Crash completing beginning of property name when preceded by JSDoc +// This test verifies that requesting completions at the beginning of a property name +// preceded by JSDoc does not cause a crash. The crash was caused by a nil pointer +// dereference when contextToken was nil. +func TestCompletionJSDocBeforePropertyNoCrash(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `export class SomeInterface { + /** ruh-roh! */ + /*a*/property: string; +} + +export class SomeClass { + /** ruh-roh! */ + /*b*/property = "value"; +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + // The primary goal of this test is to ensure no panic occurs when requesting completions. + // The testutil.RecoverAndFail defer will catch any panic and fail the test. + // We verify completions can be requested successfully without checking specific completion items. + f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{ + ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{ + CommitCharacters: &[]string{".", ",", ";"}, + EditRange: fourslash.Ignored{}, + }, + Items: &fourslash.CompletionsExpectedItems{ + Includes: []fourslash.CompletionsExpectedItem{}, + }, + }) + f.VerifyCompletions(t, "b", &fourslash.CompletionsExpectedList{ + ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{ + CommitCharacters: &[]string{".", ",", ";"}, + EditRange: fourslash.Ignored{}, + }, + Items: &fourslash.CompletionsExpectedItems{ + Includes: []fourslash.CompletionsExpectedItem{}, + }, + }) +} diff --git a/internal/ls/completions.go b/internal/ls/completions.go index 6c335b898f..8a47fd2b88 100644 --- a/internal/ls/completions.go +++ b/internal/ls/completions.go @@ -2753,7 +2753,7 @@ func getSourceFromOrigin(origin *symbolOriginInfo) string { func getRelevantTokens(position int, file *ast.SourceFile) (contextToken *ast.Node, previousToken *ast.Node) { previousToken = astnav.FindPrecedingToken(file, position) if previousToken != nil && position <= previousToken.End() && (ast.IsMemberName(previousToken) || ast.IsKeywordKind(previousToken.Kind)) { - contextToken := astnav.FindPrecedingToken(file, previousToken.Pos()) + contextToken = astnav.FindPrecedingToken(file, previousToken.Pos()) return contextToken, previousToken } return previousToken, previousToken @@ -4163,7 +4163,7 @@ func tryGetObjectTypeDeclarationCompletionContainer( return nil } // class c extends React.Component { a: () => 1\n compon| } - if isFromObjectTypeDeclaration(location) { + if contextToken != nil && isFromObjectTypeDeclaration(location) { return ast.FindAncestor(location, ast.IsObjectTypeDeclaration) } }