@@ -21,6 +21,7 @@ import {
2121 isNamedImports ,
2222 isObjectLiteralExpression ,
2323 isPropertyAssignment ,
24+ isPropertyDeclaration ,
2425 isReturnStatement ,
2526 isShorthandPropertyAssignment ,
2627 isStringLiteral ,
@@ -505,7 +506,7 @@ describe("API", { concurrency }, () => {
505506 } ) ;
506507
507508 test ( "remote declarations lazily fetch and cache binder symbols" , async ( ) => {
508- const sourceText = "function present() {}\nimport {} from './missing'; " ;
509+ const sourceText = "function present() {}" ;
509510 await using api = spawnAPI ( {
510511 "/symbols.ts" : sourceText ,
511512 } ) ;
@@ -514,9 +515,7 @@ describe("API", { concurrency }, () => {
514515 const sourceFile = await project . program . getSourceFile ( "/symbols.ts" ) ;
515516 assert . ok ( sourceFile ) ;
516517 const declaration = sourceFile . statements [ 0 ] ;
517- const withoutSymbol = sourceFile . statements [ 1 ] ;
518518 assert . ok ( isFunctionDeclaration ( declaration ) ) ;
519- assert . ok ( isImportDeclaration ( withoutSymbol ) ) ;
520519 const checkerSymbol = await project . checker . getSymbolAtPosition ( "/symbols.ts" , sourceText . indexOf ( "present" ) ) ; // @sync : const checkerSymbol = project.checker.getSymbolAtPosition("/symbols.ts", sourceText.indexOf("present"));
521520 assert . ok ( checkerSymbol ) ;
522521
@@ -537,11 +536,6 @@ describe("API", { concurrency }, () => {
537536 assert . strictEqual ( await getSymbol ( declaration ) , first ) ; // @sync : assert.strictEqual(getSymbol(declaration), first);
538537 assert . equal ( symbolRequests , 1 ) ;
539538
540- symbolRequests = 0 ;
541- await assert . rejects ( getSymbol ( withoutSymbol ) , / h a s n o b i n d e r s y m b o l / ) ; // @sync : assert.throws(() => getSymbol(withoutSymbol), /has no binder symbol/);
542- await assert . rejects ( api . getSymbol ( withoutSymbol ) , / h a s n o b i n d e r s y m b o l / ) ; // @sync : assert.throws(() => api.getSymbol(withoutSymbol), /has no binder symbol/);
543- assert . equal ( symbolRequests , 2 ) ;
544-
545539 await snapshot . dispose ( ) ;
546540 } ) ;
547541
@@ -563,7 +557,7 @@ describe("API", { concurrency }, () => {
563557
564558 test ( "source files own separate declaration result and request caches" , async ( ) => {
565559 await using api = spawnAPI ( ) ;
566- await using lease = await api . createSourceFile ( "/symbols.ts" , "function present() {}\nimport {} from './missing'; " ) ;
560+ await using lease = await api . createSourceFile ( "/symbols.ts" , "function present() {}" ) ;
567561 const file = lease . sourceFile ;
568562 assert . ok ( file instanceof RemoteSourceFile ) ;
569563 const cache = file . symbolCache ;
@@ -578,11 +572,6 @@ describe("API", { concurrency }, () => {
578572 assert . strictEqual ( cache . symbolsByDeclarationNodeIndex . get ( index ) , symbol ) ;
579573 assert . strictEqual ( cache . symbolsById . get ( symbol . reference . id ) , symbol ) ;
580574 assert . equal ( cache . declarationSymbolRequests . has ( index ) , false ) ;
581- const withoutSymbol = cast ( file . statements [ 1 ] , isImportDeclaration ) ;
582- const absentIndex = parseNodeHandle ( getNodeId ( withoutSymbol ) ) . index ;
583- await assert . rejects ( getSymbol ( withoutSymbol ) , / h a s n o b i n d e r s y m b o l / ) ; // @sync : assert.throws(() => getSymbol(withoutSymbol), /has no binder symbol/);
584- assert . equal ( cache . symbolsByDeclarationNodeIndex . has ( absentIndex ) , false ) ;
585- assert . equal ( cache . declarationSymbolRequests . has ( absentIndex ) , false ) ;
586575 api . clearSourceFileCache ( ) ;
587576 assert . strictEqual ( file . symbolCache , cache ) ;
588577 } ) ;
@@ -3953,6 +3942,69 @@ export const value = 1;
39533942 assert . notStrictEqual ( second , first ) ;
39543943 } ) ;
39553944
3945+ test ( "checker symbol methods merge declarations and parents in their project" , async ( ) => {
3946+ await using api = spawnAPI ( {
3947+ "/tsconfig.json" : JSON . stringify ( { files : [ "/src/a.ts" , "/src/b.ts" ] } ) ,
3948+ "/single/tsconfig.json" : JSON . stringify ( { files : [ "/src/a.ts" ] } ) ,
3949+ "/src/a.ts" : "namespace Merged { export const a = 1; }" ,
3950+ "/src/b.ts" : "namespace Merged { export const b = 1; }" ,
3951+ } ) ;
3952+ const snapshot = await api . createSnapshot ( { openProjects : [ "/tsconfig.json" , "/single/tsconfig.json" ] } ) ;
3953+ const project = snapshot . getConfiguredProject ( "/tsconfig.json" ) ! ;
3954+ const singleProject = snapshot . getConfiguredProject ( "/single/tsconfig.json" ) ! ;
3955+ const fileA = await project . program . getSourceFile ( "/src/a.ts" ) ;
3956+ const fileB = await project . program . getSourceFile ( "/src/b.ts" ) ;
3957+ assert . ok ( fileA && fileB ) ;
3958+ const declarationA = cast ( fileA . statements [ 0 ] , isModuleDeclaration ) ;
3959+ const declarationB = cast ( fileB . statements [ 0 ] , isModuleDeclaration ) ;
3960+ const rawA = await getSymbol ( declarationA ) ;
3961+ const rawB = await getSymbol ( declarationB ) ;
3962+ assert . notStrictEqual ( rawA , rawB ) ;
3963+
3964+ const merged = await project . checker . getSymbolOfDeclaration ( declarationA ) ;
3965+ assert . strictEqual ( await project . checker . getSymbolOfDeclaration ( declarationB ) , merged ) ;
3966+ assert . strictEqual ( await project . checker . getSymbolOfNode ( declarationA ) , merged ) ;
3967+ assert . strictEqual ( await project . checker . getMergedSymbol ( rawA ) , merged ) ;
3968+ assert . strictEqual ( await project . checker . getMergedSymbol ( rawB ) , merged ) ;
3969+ assert . strictEqual ( await project . checker . getMergedSymbol ( merged ) , merged ) ;
3970+ assert . strictEqual ( await singleProject . checker . getMergedSymbol ( rawA ) , rawA ) ;
3971+ assert . strictEqual ( await singleProject . checker . getSymbolOfDeclaration ( declarationA ) , rawA ) ;
3972+ assert . equal ( await project . checker . getSymbolOfNode ( declarationA . name ) , undefined ) ;
3973+ assert . equal ( await project . checker . getSymbolOfNode ( fileA ) , undefined ) ;
3974+ assert . equal ( await project . checker . getParentOfSymbol ( merged ) , undefined ) ;
3975+
3976+ const member = ( await rawA . getExports ( ) ) . get ( "a" as __String ) ;
3977+ assert . ok ( member ) ;
3978+ assert . strictEqual ( await member . getParent ( ) , rawA ) ;
3979+ assert . strictEqual ( await project . checker . getParentOfSymbol ( member ) , merged ) ;
3980+ assert . strictEqual ( await singleProject . checker . getParentOfSymbol ( member ) , rawA ) ;
3981+ } ) ;
3982+
3983+ test ( "checker symbol methods late-bind computed members" , async ( ) => {
3984+ await using api = spawnAPI ( {
3985+ "/src/computed.ts" : "declare const key: unique symbol;\nclass Container { [key] = 1; ordinary = 2; }\nimport {} from './missing';" ,
3986+ } ) ;
3987+ const snapshot = await api . createSnapshot ( { openFiles : [ "/src/computed.ts" ] } ) ;
3988+ const project = snapshot . getProjects ( ) [ 0 ] ;
3989+ const file = await project . program . getSourceFile ( "/src/computed.ts" ) ;
3990+ assert . ok ( file ) ;
3991+ const declaration = cast ( file . statements [ 1 ] , isClassDeclaration ) ;
3992+ const memberDeclaration = cast ( declaration . members [ 0 ] , isPropertyDeclaration ) ;
3993+ const raw = await getSymbol ( memberDeclaration ) ;
3994+ const lateBound = await project . checker . getSymbolOfDeclaration ( memberDeclaration ) ;
3995+ assert . notStrictEqual ( lateBound , raw ) ;
3996+ assert . strictEqual ( await project . checker . getSymbolOfNode ( memberDeclaration ) , lateBound ) ;
3997+ const container = await project . checker . getSymbolOfDeclaration ( declaration ) ;
3998+ assert . strictEqual ( await project . checker . getParentOfSymbol ( lateBound ) , container ) ;
3999+ const type = await project . checker . getDeclaredTypeOfSymbol ( container ) ;
4000+ const property = ( await type . getProperties ( ) ) . find ( symbol => symbol . escapedName === lateBound . escapedName ) ;
4001+ assert . ok ( property ) ;
4002+ assert . strictEqual ( await project . checker . getTargetSymbol ( property ) , lateBound ) ;
4003+ const ordinary = cast ( declaration . members [ 1 ] , isPropertyDeclaration ) ;
4004+ assert . strictEqual ( await project . checker . getSymbolOfDeclaration ( ordinary ) , await getSymbol ( ordinary ) ) ;
4005+ assert . equal ( await project . checker . getSymbolOfNode ( file . statements [ 2 ] ) , undefined ) ;
4006+ } ) ;
4007+
39564008 test ( "snapshot-owned symbols resolve cached file-owned parents" , async ( ) => {
39574009 const source = `interface Box<T> { value: T; }\ndeclare const box: Box<string>;\nbox;` ;
39584010 await using api = spawnAPI ( {
0 commit comments