Skip to content

Fix hover verbosity for nested generic namespace merges#3524

Open
Andarist wants to merge 1 commit intomicrosoft:mainfrom
Andarist:fix/hover-crash-nested-generic-namespace
Open

Fix hover verbosity for nested generic namespace merges#3524
Andarist wants to merge 1 commit intomicrosoft:mainfrom
Andarist:fix/hover-crash-nested-generic-namespace

Conversation

@Andarist
Copy link
Copy Markdown
Contributor

fixes crash found here: #3512 (comment)

if typeArgumentNodes := b.lookupInstantiatedTypeArgumentNodes(chain, index); typeArgumentNodes != nil {
return typeArgumentNodes
} else {
typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like a departure from Strada, which just called typeParametersToTypeParameterDeclarations in its lookupTypeParameterNodes; why is this change needed?

return b.f.NewNodeList(typeParameterNodes)
}
return nil
return b.typeParametersToTypeParameterNodes(symbol)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this completely fixes the issue. There are other code paths where we end up with type parameter declarations instead of type nodes (e.g. the branch immediately above this one). In Strada we already dealt with this on printing, since it was a consequence of allowing type arguments attached to identifiers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Never mind, I think this does fix everything up. I was confused because lookupInstantiatedTypeArgumentNodes has a confusing return type annotation. It would be nice to use the right type annotation (TypeList) for all of the functions involved here, to indicate intent (until we can hopefully check all this).

Copy link
Copy Markdown
Member

@gabritto gabritto Apr 23, 2026

Choose a reason for hiding this comment

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

I tested this change in Strada, and the problem is that if we use type nodes here instead of type parameter declarations, we no longer print type parameters with extends clause, e.g. List<T extends Blah> would get printed as List<T> now. So I think we just need to make the printer handle the type parameter declaration case.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a hover/QuickInfo crash and incorrect verbosity expansion behavior triggered by nested generic class/namespace merges in the TypeScript-Go language service.

Changes:

  • Fix type-parameter/type-argument node construction in NodeBuilderImpl.lookupTypeParameterNodes to avoid hover verbosity crashes.
  • Add a focused fourslash test that exercises hover verbosity expansion across a nested generic class/namespace merge.
  • Add the corresponding QuickInfo baseline output for the new test.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/checker/nodebuilderimpl.go Adjusts how type parameter nodes are produced for qualified-name printing during hover/QuickInfo generation.
internal/fourslash/tests/hoverVerbosityNestedGenericNamespaceMerge_test.go Adds regression coverage for hover verbosity expansion in the problematic nested generic namespace-merge scenario.
testdata/baselines/reference/fourslash/quickInfo/hoverVerbosityNestedGenericNamespaceMerge.baseline Adds expected hover/QuickInfo baseline output for verbosity levels 0–2.

Comment on lines +1670 to +1675
targetSymbol := b.ch.getTargetSymbol(symbol)
if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 {
return b.mapToTypeNodes(b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), false /*isBareList*/)
} else if targetSymbol.Flags&ast.SymbolFlagsFunction != 0 {
return b.mapToTypeNodes(b.ch.getTypeParametersFromDeclaration(symbol.ValueDeclaration), false /*isBareList*/)
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

In typeParametersToTypeParameterNodes, the branch for functions uses symbol.ValueDeclaration, but you already computed targetSymbol via getTargetSymbol (for instantiated symbols). For instantiated symbols, symbol.ValueDeclaration may be nil, and Checker.getTypeParametersFromDeclaration(nil) will panic. Use targetSymbol (and its ValueDeclaration / declarations) when collecting type parameters to avoid panics and ensure the flags/value come from the uninstantiated symbol.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants