diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e0e70c7294364..933c599c41a71 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1381,6 +1381,17 @@ namespace ts { return keys; } + export function getAllKeys(obj: object): string[] { + const result: string[] = []; + do { + const names = Object.getOwnPropertyNames(obj); + for (const name of names) { + pushIfUnique(result, name); + } + } while (obj = Object.getPrototypeOf(obj)); + return result; + } + export function getOwnValues(sparseArray: T[]): T[] { const values: T[] = []; for (const key in sparseArray) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 6cbc9258c0cf4..a7cb013c8eebc 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -814,7 +814,10 @@ namespace ts { let mapFromFileToProjectReferenceRedirects: Map | undefined; const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); - const structuralIsReused = tryReuseStructureFromOldProgram(); + // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks + // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`. + let structuralIsReused: StructureIsReused | undefined; + structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { processingDefaultLibFiles = []; processingOtherFiles = []; diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index bb939f7d4f1f1..6dfc909b93abd 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -83,6 +83,7 @@ namespace ts { let currentSourceFile: SourceFile; let refs: Map; let libs: Map; + let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass const resolver = context.getEmitResolver(); const options = context.getCompilerOptions(); const newLine = getNewLineCharacter(options); @@ -279,7 +280,7 @@ namespace ts { const statements = visitNodes(node.statements, visitDeclarationStatements); let combinedStatements = setTextRange(createNodeArray(transformAndReplaceLatePaintedStatements(statements)), node.statements); refs.forEach(referenceVisitor); - const emittedImports = filter(combinedStatements, isAnyImportSyntax); + emittedImports = filter(combinedStatements, isAnyImportSyntax); if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) { combinedStatements = setTextRange(createNodeArray([...combinedStatements, createEmptyExports()]), combinedStatements); } @@ -736,6 +737,12 @@ namespace ts { } const oldDiag = getSymbolAccessibilityDiagnostic; + // Setup diagnostic-related flags before first potential `cleanup` call, otherwise + // We'd see a TDZ violation at runtime + const canProduceDiagnostic = canProduceDiagnostics(input); + const oldWithinObjectLiteralType = suppressNewDiagnosticContexts; + let shouldEnterSuppressNewDiagnosticsContextContext = ((input.kind === SyntaxKind.TypeLiteral || input.kind === SyntaxKind.MappedType) && input.parent.kind !== SyntaxKind.TypeAliasDeclaration); + // Emit methods which are private as properties with no type information if (isMethodDeclaration(input) || isMethodSignature(input)) { if (hasModifier(input, ModifierFlags.Private)) { @@ -744,8 +751,7 @@ namespace ts { } } - const canProdiceDiagnostic = canProduceDiagnostics(input); - if (canProdiceDiagnostic && !suppressNewDiagnosticContexts) { + if (canProduceDiagnostic && !suppressNewDiagnosticContexts) { getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input as DeclarationDiagnosticProducing); } @@ -753,8 +759,6 @@ namespace ts { checkEntityNameVisibility(input.exprName, enclosingDeclaration); } - const oldWithinObjectLiteralType = suppressNewDiagnosticContexts; - let shouldEnterSuppressNewDiagnosticsContextContext = ((input.kind === SyntaxKind.TypeLiteral || input.kind === SyntaxKind.MappedType) && input.parent.kind !== SyntaxKind.TypeAliasDeclaration); if (shouldEnterSuppressNewDiagnosticsContextContext) { // We stop making new diagnostic contexts within object literal types. Unless it's an object type on the RHS of a type alias declaration. Then we do. suppressNewDiagnosticContexts = true; @@ -909,13 +913,13 @@ namespace ts { return cleanup(visitEachChild(input, visitDeclarationSubtree, context)); function cleanup(returnValue: T | undefined): T | undefined { - if (returnValue && canProdiceDiagnostic && hasDynamicName(input as Declaration)) { + if (returnValue && canProduceDiagnostic && hasDynamicName(input as Declaration)) { checkName(input as DeclarationDiagnosticProducing); } if (isEnclosingDeclaration(input)) { enclosingDeclaration = previousEnclosingDeclaration; } - if (canProdiceDiagnostic && !suppressNewDiagnosticContexts) { + if (canProduceDiagnostic && !suppressNewDiagnosticContexts) { getSymbolAccessibilityDiagnostic = oldDiag; } if (shouldEnterSuppressNewDiagnosticsContextContext) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index f12506be2fb27..8f1a5ae15280d 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -344,7 +344,8 @@ namespace FourSlash { "getDocumentHighlights", ]; const proxy = {} as ts.LanguageService; - for (const k in ls) { + const keys = ts.getAllKeys(ls); + for (const k of keys) { const key = k as keyof typeof ls; if (cacheableMembers.indexOf(key) === -1) { proxy[key] = (...args: any[]) => (ls[key] as Function)(...args);