Skip to content

Commit 9b72589

Browse files
Improve declaration emit type safety.
Signed-off-by: Titian Cernicova-Dragomir <[email protected]>
1 parent 7067307 commit 9b72589

File tree

7 files changed

+124
-177
lines changed

7 files changed

+124
-177
lines changed

src/compiler/transformers/declarations.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
ConstructorTypeNode,
2424
ConstructSignatureDeclaration,
2525
contains,
26+
CoreEmitResolver,
2627
createDiagnosticForNode,
2728
createDiagnosticForRange,
2829
createEmptyExports,
@@ -138,7 +139,7 @@ import {
138139
isMethodSignature,
139140
isModifier,
140141
isModuleDeclaration,
141-
IsolatedEmitResolver,
142+
IsolatedTransformationContext,
142143
isOmittedExpression,
143144
isPrivateIdentifier,
144145
isPropertySignature,
@@ -293,7 +294,7 @@ const declarationEmitNodeBuilderFlags = NodeBuilderFlags.MultilineObjectLiterals
293294
*
294295
* @internal
295296
*/
296-
export function transformDeclarations(context: TransformationContext) {
297+
export function transformDeclarations(context: (TransformationContext & { isIsolatedDeclarationContext?: undefined }) | IsolatedTransformationContext) {
297298
const throwDiagnostic = () => Debug.fail("Diagnostic emitted without context");
298299
let getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic = throwDiagnostic;
299300
let needsDeclare = true;
@@ -325,7 +326,7 @@ export function transformDeclarations(context: TransformationContext) {
325326

326327
function createTransformerServices(): {
327328
isolatedDeclarations: true;
328-
resolver: IsolatedEmitResolver;
329+
resolver: CoreEmitResolver;
329330
localInferenceResolver: LocalInferenceResolver;
330331
host: undefined;
331332
symbolTracker: undefined;
@@ -338,24 +339,28 @@ export function transformDeclarations(context: TransformationContext) {
338339
symbolTracker: SymbolTracker;
339340
ensureNoInitializer: (node: CanHaveLiteralInitializer) => Expression | undefined;
340341
} {
341-
const { isolatedDeclarations, resolver: localInferenceResolver } = createLocalInferenceResolver({
342-
ensureParameter,
343-
context,
344-
visitDeclarationSubtree,
345-
setEnclosingDeclarations(node) {
346-
const oldNode = enclosingDeclaration;
347-
enclosingDeclaration = node;
348-
return oldNode;
349-
},
350-
checkEntityNameVisibility(name, container) {
351-
return checkEntityNameVisibility(name, container ?? enclosingDeclaration);
352-
},
353-
});
342+
const isolatedDeclarations = context.getCompilerOptions().isolatedDeclarations;
343+
// DTE Context or in isolated declarations mode
344+
if (context.isIsolatedDeclarationContext || isolatedDeclarations) {
345+
Debug.assert(isolatedDeclarations)
346+
const localInferenceResolver = createLocalInferenceResolver({
347+
ensureParameter,
348+
context,
349+
visitDeclarationSubtree,
350+
setEnclosingDeclarations(node) {
351+
const oldNode = enclosingDeclaration;
352+
enclosingDeclaration = node;
353+
return oldNode;
354+
},
355+
checkEntityNameVisibility(name, container) {
356+
return checkEntityNameVisibility(name, container ?? enclosingDeclaration);
357+
},
358+
});
354359

355-
if (isolatedDeclarations) {
356-
const resolver: IsolatedEmitResolver = context.getEmitResolver();
360+
361+
const resolver: CoreEmitResolver = context.getEmitResolver();
357362
// Ideally nothing should require the symbol tracker in isolated declarations mode.
358-
// createLiteralConstValue is teh one exception
363+
// createLiteralConstValue is the one exception
359364
const emptySymbolTracker = {};
360365
return {
361366
isolatedDeclarations,
@@ -376,8 +381,8 @@ export function transformDeclarations(context: TransformationContext) {
376381
const resolver = context.getEmitResolver();
377382
const symbolTracker: SymbolTracker = createSymbolTracker(resolver, host);
378383
return {
379-
isolatedDeclarations,
380-
localInferenceResolver,
384+
isolatedDeclarations: false,
385+
localInferenceResolver: undefined,
381386
resolver,
382387
symbolTracker,
383388
host,
@@ -626,6 +631,7 @@ export function transformDeclarations(context: TransformationContext) {
626631
libs = new Map();
627632
existingTypeReferencesSources = node.sourceFiles;
628633
let hasNoDefaultLib = false;
634+
Debug.assert(!isolatedDeclarations, "Bundles are not supported in isolated declarations")
629635
const bundle = factory.createBundle(
630636
map(node.sourceFiles, sourceFile => {
631637
if (sourceFile.isDeclarationFile) return undefined!; // Omit declaration files from bundle results, too // TODO: GH#18217
@@ -648,7 +654,7 @@ export function transformDeclarations(context: TransformationContext) {
648654
sourceFile,
649655
[factory.createModuleDeclaration(
650656
[factory.createModifier(SyntaxKind.DeclareKeyword)],
651-
factory.createStringLiteral(getResolvedExternalModuleName(context.getEmitHost(), sourceFile)),
657+
factory.createStringLiteral(getResolvedExternalModuleName(host, sourceFile)),
652658
factory.createModuleBlock(setTextRange(factory.createNodeArray(transformAndReplaceLatePaintedStatements(statements)), sourceFile.statements)),
653659
)],
654660
/*isDeclarationFile*/ true,
@@ -1085,7 +1091,7 @@ export function transformDeclarations(context: TransformationContext) {
10851091
if (isBundledEmit) {
10861092
// Bundle emit not supported for isolatedDeclarations
10871093
if (!isolatedDeclarations) {
1088-
const newName = getExternalModuleNameFromDeclaration(context.getEmitHost(), resolver, parent);
1094+
const newName = getExternalModuleNameFromDeclaration(host, resolver, parent);
10891095
if (newName) {
10901096
return factory.createStringLiteral(newName);
10911097
}

src/compiler/transformers/declarations/emitResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
appendIfUnique,
33
bindSourceFileForDeclarationEmit,
44
ComputedPropertyName,
5+
CoreEmitResolver,
56
createEvaluator,
67
Debug,
78
Declaration,
@@ -50,7 +51,6 @@ import {
5051
isInJSFile,
5152
isLateVisibilityPaintedStatement,
5253
isNumericLiteral,
53-
IsolatedEmitResolver,
5454
isPartOfTypeNode,
5555
isPrefixUnaryExpression,
5656
isPropertyAccessExpression,
@@ -87,7 +87,7 @@ import {
8787

8888

8989
/** @internal */
90-
export function createEmitDeclarationResolver(file: SourceFile): IsolatedEmitResolver {
90+
export function createEmitDeclarationResolver(file: SourceFile): CoreEmitResolver {
9191
const { getNodeLinks, resolveMemberKey, resolveName, resolveEntityName } = bindSourceFileForDeclarationEmit(file);
9292

9393
function getEnumValueFromName(name: PropertyName | NoSubstitutionTemplateLiteral, location: EnumDeclaration) {

src/compiler/transformers/declarations/localInferenceResolver.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ClassExpression,
66
createDiagnosticForNode,
77
createPropertyNameNodeForIdentifierOrLiteral,
8+
Debug,
89
DiagnosticMessage,
910
Diagnostics,
1011
EntityNameOrEntityNameExpression,
@@ -29,6 +30,7 @@ import {
2930
isMethodDeclaration,
3031
isNoSubstitutionTemplateLiteral,
3132
isNumericLiteral,
33+
IsolatedTransformationContext,
3234
isOmittedExpression,
3335
isOptionalDeclaration,
3436
isParameter,
@@ -113,39 +115,34 @@ export function createLocalInferenceResolver({
113115
visitDeclarationSubtree(input: Node): VisitResult<Node | undefined>;
114116
checkEntityNameVisibility(name: EntityNameOrEntityNameExpression, container?: Node): void;
115117
ensureParameter(p: ParameterDeclaration): ParameterDeclaration;
116-
context: TransformationContext;
117-
}): { resolver: LocalInferenceResolver; isolatedDeclarations: true; } | { resolver: undefined; isolatedDeclarations: false; } {
118+
context: IsolatedTransformationContext | TransformationContext;
119+
}): LocalInferenceResolver {
118120
let currentSourceFile: SourceFile;
119121
const options = context.getCompilerOptions();
120122
const resolver = context.getEmitResolver();
121-
if (!options.isolatedDeclarations) {
122-
return { resolver: undefined, isolatedDeclarations: false };
123-
}
123+
Debug.assert(options.isolatedDeclarations, "createLocalInferenceResolver");
124124
const { factory } = context;
125125
const strictNullChecks = !!options.strict || !!options.strictNullChecks;
126126

127-
return {
128-
resolver: {
129-
fromInitializer(node: HasInferredType, type: TypeNode | undefined, sourceFile: SourceFile) {
130-
const oldSourceFile = currentSourceFile;
131-
currentSourceFile = sourceFile;
132-
try {
133-
const localType = localInferenceFromInitializer(node, type);
134-
if (localType !== undefined) {
135-
return localType;
136-
}
137-
if (type) {
138-
return visitNode(type, visitDeclarationSubtree, isTypeNode)!;
139-
}
140-
return makeInvalidType();
127+
return {
128+
fromInitializer(node: HasInferredType, type: TypeNode | undefined, sourceFile: SourceFile) {
129+
const oldSourceFile = currentSourceFile;
130+
currentSourceFile = sourceFile;
131+
try {
132+
const localType = localInferenceFromInitializer(node, type);
133+
if (localType !== undefined) {
134+
return localType;
141135
}
142-
finally {
143-
currentSourceFile = oldSourceFile;
136+
if (type) {
137+
return visitNode(type, visitDeclarationSubtree, isTypeNode)!;
144138
}
145-
},
146-
makeInvalidType,
139+
return makeInvalidType();
140+
}
141+
finally {
142+
currentSourceFile = oldSourceFile;
143+
}
147144
},
148-
isolatedDeclarations: options.isolatedDeclarations,
145+
makeInvalidType,
149146
};
150147
function hasParseError(node: Node) {
151148
return !!(node.flags & NodeFlags.ThisNodeHasError);

src/compiler/transformers/declarations/transpileDeclaration.ts

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
Diagnostic,
1212
EmitHost,
1313
ensureTrailingDirectorySeparator,
14-
factory,
1514
getAreDeclarationMapsEnabled,
1615
getBaseFileName,
1716
getDeclarationEmitOutputFilePathWorker,
@@ -22,74 +21,44 @@ import {
2221
getSourceFilePathInNewDir,
2322
normalizePath,
2423
normalizeSlashes,
24+
nullTransformationContext,
2525
PrinterOptions,
2626
SourceFile,
2727
SourceMapGenerator,
2828
sys,
29-
TransformationContext,
3029
transformDeclarations,
3130
TranspileDeclarationsOptions,
3231
TranspileDeclarationsOutput,
3332
} from "../../_namespaces/ts";
3433

35-
function createEmitDeclarationHost(options: TranspileDeclarationsOptions): EmitHost {
36-
const throws = () => Debug.fail("Function should not be called in isolated declarations emit");
37-
return {
38-
getCurrentDirectory: () => options.currentDirectory ?? ".",
39-
getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames),
40-
useCaseSensitiveFileNames: () => !!options.useCaseSensitiveFileNames,
41-
getCompilerOptions: () => options.compilerOptions,
42-
getCommonSourceDirectory: () => ensureTrailingDirectorySeparator(options.commonSourceDirectory ?? "."),
43-
get redirectTargetsMap(): never {
44-
Debug.fail("redirectTargetsMap should not be used in isolated declarations");
45-
return undefined!; // Need return despite fail call GH#52214
46-
},
47-
directoryExists: throws,
48-
fileExists: throws,
49-
readFile: throws,
50-
realpath: throws,
51-
getLibFileFromReference: throws,
52-
getSourceFileFromReference: throws,
53-
isSourceOfProjectReferenceRedirect: throws,
54-
55-
getSourceFiles: throws,
56-
isEmitBlocked: throws,
57-
getPrependNodes: throws,
58-
writeFile: throws,
59-
getBuildInfo: throws,
60-
getSourceFile: throws,
61-
getSourceFileByPath: throws,
62-
getProjectReferenceRedirect: throws,
63-
getFileIncludeReasons: throws,
64-
isSourceFileFromExternalLibrary: throws,
65-
getResolvedProjectReferenceToRedirect: throws,
66-
};
67-
}
68-
6934
export function transpileDeclaration(sourceFile: SourceFile, transpileOptions: TranspileDeclarationsOptions): TranspileDeclarationsOutput {
7035
const compilerOptions: CompilerOptions = {
7136
...transpileOptions.compilerOptions,
7237
isolatedDeclarations: true,
7338
traceResolution: false,
7439
};
75-
const emitHost = createEmitDeclarationHost(transpileOptions);
40+
const emitHost = {
41+
getCurrentDirectory: () => transpileOptions.currentDirectory ?? ".",
42+
getCanonicalFileName: createGetCanonicalFileName(!!compilerOptions.useCaseSensitiveFileNames),
43+
useCaseSensitiveFileNames: () => !!compilerOptions.useCaseSensitiveFileNames,
44+
getCompilerOptions: () => compilerOptions.compilerOptions,
45+
getCommonSourceDirectory: () => ensureTrailingDirectorySeparator(transpileOptions.commonSourceDirectory ?? "."),
46+
};
7647
const emitResolver = createEmitDeclarationResolver(sourceFile);
7748
const diagnostics: Diagnostic[] = [];
7849
const transformer = transformDeclarations({
79-
getEmitHost() {
80-
return emitHost;
81-
},
50+
isIsolatedDeclarationContext: true,
51+
...nullTransformationContext,
8252
getEmitResolver() {
8353
return emitResolver;
8454
},
8555
getCompilerOptions() {
86-
return emitHost.getCompilerOptions();
56+
return compilerOptions;
8757
},
88-
factory,
8958
addDiagnostic(diag: any) {
9059
diagnostics.push(diag);
9160
},
92-
} as TransformationContext);
61+
});
9362
const result = transformer(sourceFile);
9463

9564
const printer = createPrinter({
Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,21 @@
11
import {
2-
AccessorDeclaration,
3-
AllAccessorDeclarations,
4-
AnyImportSyntax,
5-
ComputedPropertyName,
6-
Declaration,
7-
ElementAccessExpression,
8-
EntityNameOrEntityNameExpression,
9-
EnumMember,
10-
Expression,
11-
FunctionDeclaration,
12-
ImportDeclaration,
13-
LateBoundDeclaration,
14-
Node,
15-
ParameterDeclaration,
16-
PropertyAccessExpression,
17-
PropertyDeclaration,
18-
PropertySignature,
19-
ResolutionMode,
20-
SignatureDeclaration,
21-
StringLiteralLike,
22-
Symbol,
23-
SymbolTracker,
24-
SymbolVisibilityResult,
25-
VariableDeclaration,
2+
CompilerOptions,
3+
CoreEmitResolver,
4+
CoreTransformationContext,
5+
Diagnostic,
6+
NodeFactory,
267
} from "../../_namespaces/ts";
278

9+
/** @internal */
10+
export interface IsolatedTransformationContext extends CoreTransformationContext {
11+
isIsolatedDeclarationContext: true;
12+
getEmitResolver(): CoreEmitResolver;
13+
getCompilerOptions(): CompilerOptions;
14+
factory: NodeFactory;
15+
addDiagnostic(diag: Diagnostic): void;
16+
}
17+
2818
/** @internal */
2919
export type MemberKey = string & {
3020
__memberKey: void;
3121
};
32-
33-
/** @internal */
34-
export interface IsolatedEmitResolver {
35-
isLiteralComputedName(node: ComputedPropertyName): boolean;
36-
isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean;
37-
isLateBound(node: Declaration): node is LateBoundDeclaration;
38-
isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined;
39-
isExpandoFunction(node: VariableDeclaration | FunctionDeclaration): boolean;
40-
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression;
41-
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
42-
isOptionalParameter(node: ParameterDeclaration): boolean;
43-
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode | undefined][] | undefined;
44-
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
45-
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
46-
isImportRequiredByAugmentation(decl: ImportDeclaration): boolean;
47-
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
48-
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
49-
tryFindAmbientModule(moduleReferenceExpression: Expression): Symbol | undefined;
50-
getPropertiesOfContainerFunction(node: FunctionDeclaration | VariableDeclaration): Symbol[]
51-
}

0 commit comments

Comments
 (0)