Create serializable synthetic call-signature declarations

Co-authored-by: weswigham <2932786+weswigham@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]andweswigham authored and GitHub committed 2026-10-21 18:57:35 +00:00
1 parent 869dce4097
commit a6da8d9eab
5 files changed
+61 -6

No files matched your search

+11 -1
View File
@@ -4967,7 +4967,17 @@ describe("Checker - getContextualType", { concurrency }, () => {
const signatures = await project.checker.getSignaturesOfType(type, SignatureKind.Call);
assert.equal(signatures.length, 1);
assert.deepEqual((await signatures[0].getParameters()).map(parameter => parameter.name), ["target", "context"]);
assert.equal(signatures[0].declaration, undefined);
const declarationHandle = signatures[0].declaration;
assert.ok(declarationHandle);
assert.equal(declarationHandle.kind, SyntaxKind.FunctionType);
const declaration = await declarationHandle.resolve();
assert.ok(declaration);
assert.ok(isSignatureDeclaration(declaration));
assert.equal(declaration.kind, SyntaxKind.FunctionType);
const declarationSignature = await project.checker.getSignatureFromDeclaration(declaration);
assert.ok(declarationSignature);
assert.strictEqual(declarationSignature.declaration, declarationHandle);
assert.deepEqual(await project.program.getSourceFileNames(), ["/src/main.ts"]);
});
test("contextual type from function parameter", async () => {
+11 -1
View File
@@ -4792,7 +4792,17 @@ describe("Checker - getContextualType", { concurrency }, () => {
const signatures = project.checker.getSignaturesOfType(type, SignatureKind.Call);
assert.equal(signatures.length, 1);
assert.deepEqual((signatures[0].getParameters()).map(parameter => parameter.name), ["target", "context"]);
assert.equal(signatures[0].declaration, undefined);
const declarationHandle = signatures[0].declaration;
assert.ok(declarationHandle);
assert.equal(declarationHandle.kind, SyntaxKind.FunctionType);
const declaration = declarationHandle.resolve();
assert.ok(declaration);
assert.ok(isSignatureDeclaration(declaration));
assert.equal(declaration.kind, SyntaxKind.FunctionType);
const declarationSignature = project.checker.getSignatureFromDeclaration(declaration);
assert.ok(declarationSignature);
assert.strictEqual(declarationSignature.declaration, declarationHandle);
assert.deepEqual(project.program.getSourceFileNames(), ["/src/main.ts"]);
});
test("contextual type from function parameter", () => {
+13 -3
View File
@@ -501,8 +501,8 @@ func (sd *snapshotData) newSignatureResponse(projectID project.Handle, sig *chec
Flags: uint32(sig.Flags()),
}
if declaration := sig.Declaration(); declaration != nil && ast.GetSourceFileOfNode(declaration) != nil {
resp.Declaration = sd.nodeHandleFrom(declaration)
if sig.Declaration() != nil {
resp.Declaration = sd.nodeHandleFrom(sig.Declaration())
}
if len(sig.TypeParameters()) > 0 {
@@ -2362,7 +2362,11 @@ func (s *Session) handleGetSourceFile(ctx context.Context, params *GetSourceFile
return nil, err
}
return s.encodeSourceFileResponse(program.GetSourceFile(params.File.ToFileName(program.BaseDirectory())))
fileName := params.File.ToFileName(program.BaseDirectory())
if fileName == checker.SyntheticSignatureFileName {
return s.encodeSourceFileResponse(checker.GetSyntheticSignatureSourceFile())
}
return s.encodeSourceFileResponse(program.GetSourceFile(fileName))
}
// handleGetConfigFileNames returns tsconfig file names associated with the project's command line.
@@ -5029,6 +5033,12 @@ func (sd *snapshotData) resolveNodeHandle(program *compiler.Program, handle Node
return nil, fmt.Errorf("%w: invalid node handle %q", ErrClientError, handle)
}
sourceFile := program.GetSourceFileByNodeID(ast.NodeId(nodeID))
if sourceFile == nil {
syntheticFile := checker.GetSyntheticSignatureSourceFile()
if sourceFileNodeID(syntheticFile) == nodeID {
sourceFile = syntheticFile
}
}
if sourceFile == nil {
return nil, fmt.Errorf("%w: node handle %q could not be resolved (file may not be loaded or handle may be stale)", ErrClientError, handle)
}
+1 -1
View File
@@ -31161,7 +31161,7 @@ func (c *Checker) newSetterFunctionType(t *Type) *Type {
// Creates a synthetic `Signature` corresponding to a call signature.
func (c *Checker) newCallSignature(typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, returnType *Type) *Signature {
decl := c.factory.NewFunctionTypeNode(nil, nil, c.factory.NewKeywordTypeNode(ast.KindAnyKeyword))
decl := GetSyntheticSignatureSourceFile().Statements.Nodes[0].Type()
return c.newSignature(SignatureFlagsNone, decl, typeParameters, thisParameter, parameters, returnType, nil, len(parameters))
}
@@ -0,0 +1,25 @@
package checker
import (
"sync"
"github.com/microsoft/TypeScript/tsc/internal/ast"
"github.com/microsoft/TypeScript/tsc/internal/core"
"github.com/microsoft/TypeScript/tsc/internal/parser"
"github.com/microsoft/TypeScript/tsc/internal/tspath"
)
const SyntheticSignatureFileName tspath.RootedFilePath = "tsgo://synthetic/signatures.d.ts"
var syntheticSignatureSourceFile = sync.OnceValue(func() *ast.SourceFile {
return parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: SyntheticSignatureFileName,
PathKey: tspath.CaseSensitive.PathKey(tspath.RootedPath(SyntheticSignatureFileName)),
}, "type SyntheticSignature = () => any;", core.ScriptKindTS)
})
// GetSyntheticSignatureSourceFile provides an immutable, shared AST for synthetic
// call-signature declarations. It is not an input file of any program.
func GetSyntheticSignatureSourceFile() *ast.SourceFile {
return syntheticSignatureSourceFile()
}